문서를 정리하다가 전에 정리된 문서에서 아래와 같은 내용이 있어서 포스팅 합니다..
단, 출처는 생각이 안 나네요. ^^;;
The following set of command line options are recommended for running high performance server applications:
-XX:+AggressiveOpts
-XX:+AggressiveHeap
-XX:ParallelGCThreads=2
-XX:InterpreterProfilePercentage=10
-XX:CompileThreshold=35000
-XX:OnStackReplacePercentage=80
-XX:PermSize=256m
-XX:MaxPermSize=256m
-XX:AllocatePrefetchStepSize=128
-XX:AllocatePrefetchStyle=2
-XX:AllocatePrefetchDistance=1024
-XX:+UseCompressedOops if heap size is less than 32GB
-Xmx, -Xms, -Xmn should have sufficiently large values.
(For example, -Xms6600m -Xmn5000m -Xmx6600m or -Xms20000m -Xmn18000m -Xmx20000m.)
This driver converts JDBC calls to ODBC calls through JDBC-ODBC Bridge driver which in turn converts to database calls. Client requires ODBC libraries.
2
Two
Native API - Partly -
Java driver
This driver converts JDBC calls to database specific native calls. Client requires database specific libraries.
3
Three
JDBC - Net -All Java
driver
This driver passes calls to proxy server through network protocol which in turn converts to database calls and passes through database specific protocol. Client doesn't require any driver.
4
Two
Native protocol - All -
Java driver
This driver directly calls database. Client doesn't require any driver.
외부 API들이 JDK에 많이 포함이 되어 있습니다. http://java.sun.com/javase/6/docs/api/ 문서의 package들을 보시면, javax나 org로 시작하는 외부(?) API들을 보실 수가 있는데요.. JDK에서는 rt.jar에 포함되어 잇지만, 아래의 내용처럼, http://aploit.egloos.com/4806304 님의 블로그에 포스팅된 내용을 보시면, 최신의 외부 API를 사용하기 위해서 java.endorsed.dirs 옵션을 사용해서 해결하는 내용을 보실 수가 있습니다.
java로 작업을 하다보면 가끔씩 endorsed라는 디렉토리 명이 보였다. 사전을 찾아보면 배서하다, 추천하다, 양도하다 정도의
뜻이고, 사전적 의미가 아닌 java쪽에서는 어떤 의미로 사용되는지 몰랐다.
그냥 몰라도 별 문제는 없었고 해서
그냥 넘어갔었다.
그런데 의미를 확실히 알게 되었다.
jdk1.4에는 JAXP1.2의 라이브러리들이
포함되어 있다. 대표적으로 org.w3c.dom의 패키지와 javax.xml의 패키지가 그것이다.
그런데
JAXP1.3, 1.4가 나왔고, jdk1.4의 환경에서 이들을 사용하고자 할 경우에 문제가 된다.
(참고로 JAXP1.3은
JDK5.0에 JAXP1.4는 JDK6.0에 포함되었다. 따라서 JDK5.0에서는 JAXP1.3 라이브러리를 따로 설치할 필요가
없다.) jdk는 1.4이고 어플리케이션에서 JAXP1.3을 설치했다고 하면(구체적인 jar 파일은 jaxp-api.jar와
jaxp-ri.jar이다) 같은 패키지 이름까지 같은 클래스가 2개 존재하게된다. 예를 들면
org.w3c.dom.Document이다. 하나는 rt.jar에 있는 것이고(버전은 JAXP1.2) 하나는
jaxp-api.jar에 있는 것이다(요 버전은 JAXP1.3). 원했던 것은 rt.jar의 것이 아닌 jaxp-api.jar의
것이 로딩되는것이였는데, 그렇게 동작하지 않는다. 설치한 라이브러리들 간의 로딩 순서는 classpath에 설정한 순서로 조절할 수
있지만, rt.jar는 설정 순서로 조절할 수 없다.
JDK 자체에 포함된 것이 아닌 설치한 라이브러리의 것을
로딩할때 사용하는 매커니즘이 바로 endorsed이다. "java.endorsed.dirs"라는 시스템 파라매터를 설정하면 된다.
다음과 같이 java를 실행할 때 jaxp-api.jar가 있는 디렉토리를 설정해 주면 된다.
java
-Djava.endorsed.dirs=/path/where/jar/is ......
여기서 /path/where/jar/is
디렉토리는 jaxp-api.jar가 있는 곳이다.
TOMCAT의 경우 endorsed 매카니즘을 사용하고 있다.
tomcat 4.1.39의 bin/setclasspath.bat에서 set
JAVA_ENDORSED_DIRS=%BASEDIR%\common\endorsed 으로 endorsed 디렉토리를 설정하고
catalina.bat에서 다음과 같이 java 실행 시에 시스템 파라매터로 설정해 주고 있다.
%_EXECJAVA% ...
-Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%"
TOMCAT_HOME/common
/endorsed에 가보면 xercesImpl.jar, xml-apis.jar가 있는데 이들은 JAXP 1.3의 것이다.
참
고로 클래스로딩의 매카니즘에서 "java." 로 시작하는 패키지의 클래스는 endorsed가 적용되지 않는다.
JAXP
에 대한 기타. JAXP1.4가 설치되어 있다면 apache xalan의 다음과 같은 라이브러리들을 따로
설치할 필요 없다. JAXP안에 포함되어 있기 때문이다. 설치할 필요가 없는 것 보다는 쫑나는 상황을 피하기 위하여 설치해서는
안된다.
serializer.jar
xalan.jar
xercesImpl.jar
xml-apis.jar
xsltc.jar
유명한 Doug Lea 교수님의 멀티 쓰레드 프로그램의 평가기준에 대한 내용입니다. "Java 언어로 배우는 디자인 패턴
입문 멀티 쓰레드편, Yuki Hiroshi" 의 Introduction 2 챕터에 나오는 내용입니다. 아래 내용의 출처는 http://ethdemor.springnote.com/pages/4349969 입니다.
아래의 기준들은 Doug Lea 분류를 기준으로 한 것이며, 이런 류의 평가 기준은 대개 상반(trade-off)되는 경우가 있습니다.
필 수
안전성 (safety) - 객체를 망가트리지 않을 것
- "망가 진다"는 것은 비유적 표현으로 객체의 필드가 예상 외의 값을 취하는 것을 의미합니다.
- 복수의 스레드가 이용해도 안전성이 유지되는 클래스를 스레드 세이프(thread-safe)한 클래스라고 하며 자바 API에는 명시되어 있습니다.
생존성 (liveness) - 필요한 처리가 이뤄질 것
옵 션
재사용성 (resuability) - 클래스를 다시 사용할 수 있는 것
- 멀티 스레드 프로그램에서는 스레드 배타제어, 구조, 방침을 캡슐화 하는 것이 관건이며 Java SE 5.0의 java.util.concurrent 패키지에서 이를 잘 실현하고 있습니다.
- 수행 능력(performance) - 고속 / 대량으로 처리할 수 있을 것
- throughput - 단위 시간 당 처리 수
- 응답성 (responsiveness) - 요청을 한 뒤 반응이 나타날 때까지 걸리는 시간
- GUI 프로그램에서는 요청의 완료까지 걸리는 시간보다 처리 시작까지의 시간을 중요하게 생각하기도 합니다. 여기서 완료는 요청 -> 처리 -> 응답 까지의 시간이며 처 리 시작은 완료까지의 시간은 좀 더 걸리더라도 클라이언트에게 먼저 응답(~을 처리 중입니다 등)을 보내는 것을 말합니다.
- 응답성이 높은 프로그램은 대기 시간(latency)이 짧다고 표현합니다.
- 용량 (capacity) - 동시에 처리할 수 있는 수
- 그 밖에 효율(efficiency), 확장성(scalability), 저하(degradation) 등이 있습니다.
평가기준 정리
멀티 스레드 시스템에서 안전성과 생존성이 지켜지지 않으면 제대로된 프로그램이라고 할 수 없습니다.
따라서 이 필수 조건을 만족한 상태에서 재사용성과 수행 능력을 어떻게 높이느냐가 관건이 되겠습니다.
자바의 Garbage Collector 이슈는 성능과 더불어 자원 해제라는 막중한 역할을 하기 때문에 중요하게 파악을 해 둬야 되는 내용입니다. 아래는 자바에서 지원하고 있는 Garbage Collector의 영역별 내용과 형태에 대한 내용입니다.
- serial collector : young, old 영역,
java 1.4 버전까지 default collector, single cpu만
사용 - parallel collector : young 영역, java
5 버전부터 CPU 2, MEM 2G 이상(Hot
Spot VM)일
경우 자동선택
- parallel compacting collector : old 영역, option으로 지정해야 선택됨
- cms(concurrent mark-sweep) collector : old 영역, option으로 지정해야 선택됨.
- G1 collector:
young, old 영역, 1.6.0_14(NUMA
구조 지원, 1.6.0_18 버전에
성능에 대한 언급) 부터 사용이 가능합니다.
option으로
지정해야 선택됨. “early access software”, 자바 비즈니스
라이선스를 구매하지 않은
고객은 사용을 하지 않는 것이 좋다고 합니다. Java 7버전을 타켓으로 하고
있다고 합니다.
Circular Dependency의 해악은 개발을 하다보면 자연스럽게 알게 된다..
회사에서 개발하는 시스템중에 처음에는 그러하지 않았을 것인데, 아키텍처링을 할 수 없는 지금의 조직에서 자연스럽게 Circular Dependency를 걸어버렸다.. 일정이라는 최고의 명제 아래 말이다..
결국, 의존과 버전에 묶여서 뗄레야 뗄루 없는 커플처럼 되어 버린 기존의 시스템은 또다시 거대한 레거시로의 한발한발 다가서고 있다.. 정말로 뗄 수 없을 정도로 말이다..
누군가는 나서서 해결을 해야 겠지만, 누가 하겠는가.. 일정에 맞춰서 들어온 일 처리해야쥐..
결국 시스템의 모습은 직책자들의 마인드와 의지가 만들어 내는 우리의 현실이며, 이 모습은 좋은 개발팀 혹은 좋은 개발실은 아니라는 느낌을 지울 수 없다..
아래 Building속성에서의 Circular dependencies는 Warning이 아니라 Error 상황이 되어야 된다.
그래서, 위의 상황을 미연에 방지를 해야된다.. 단순한, 일정에 맞춰야 되서 Warning이 Default가 되어서는 안될 것이다.. ^^;;
소켓을 기반으로 풀링을 하는 라이브러리를 개발할 시에 보통 소켓 클래스의 isconnected 라는 상태변수를 통해서 소켓의 연결상태를 확인할 수 있으나, 정확한 정보는 아니죠.. isconnected라는 변수는, 마지막 네트웍 이벤트를 처리한 소켓의 연결 상태이기 때문이죠.. 그래서 확실하게 풀을 관리하기 위해서는 네트웍 이벤트를 발생시켜 보는것이 Java에서는 해결책이라고 봐야 될듯 합니다.
아래코드는 SocketChannel이 non-block인 상태를 기준으로, read 메쏘드를 호출했을 경우, 리턴값을 보고 상태를 확인하는 코드입니다.
int readbytes = socketChannel.read(buf);
// 연결이 되어 있음..
if(readbytes == 0)
// 끊어졌네요.
if (readbytes == -1)
// 리시브된 데이타가 있네요..
if(readbytes >0)
소켓 풀링을 관리하는 매니저 클래스가 풀에 들어있는 소켓의 read 이벤트를 주기적으로 호출해보면, socket의 연결상태를 잘 유지할 수 있을 것입니다. ^^
보통 객체에 대한 고유값으로 hashCode() 메쏘드를 많이 사용하는데요.....
hashCode()는 Override가 가능하기 때문에, 고유값이 아닐경우가 잠재적으로 존재하게 됩니다.
그런 문제상황(?)을 해결할 수 있도록 System.identityHashCode(Object x)라는 메쏘드가 존재하고 있네요..
Code Cache: contains memory used for compilation and storage of native code
Eden Space: pool from which memory is initially allocated for most objects
Survivor Space: pool containing objects that have survived Eden space garbage collection
Tenured Gen: pool containing long-lived objects
Perm Gen: contains reflective data of the JVM itself, including class and memory objects
Perm Gen [shared-ro]: read-only reflective data
Perm Gen [shared-rw]: read-write reflective data
// second
long second = TimeUnit.SECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS);
// millisecond
long second = TimeUnit.MILLISECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS);
며칠전에 사석에서 상사분이 Tomcat은 IIS에 비해서 connection을 적게 받고, 그로 인해서 성능이 떨어진다고 말씀을 하시네요.. ^^;; 서비스의 전체적인 성능 문제가 Java 및 Tomcat에 기인을 하고 있다고 생각을 하는것 같아서.. 같은 개발자 입장에서 타 분야에 대한 경험으로 얘기하는게 아니라 말 그대로 선입견(Overlapped I/O의 성능만 말씀하시네 ^^;;)으로 얘기를 하는 듯한 느낌이었습니다.
서비스의 성능저하는 Tomcat이나 IIS보다는 그 위에 올라가는 어플이 더 성능에 영향을 미칠텐데 말입니다. ^^;;
그래서 IIS 기반의 ASP.NET 서비스들이 주로 사용하는 DataSet에 대해서 Serialize 데이타에 대해서 살펴보았습니다. 비교는 단순하게 Java , C# Object, C# DataSet으로 10개의 리스트를 가지는 모델을 가지고 하였습니다.
아래의 코드로 Java에서의 Object Array, C#에서의 Object Array, C#에서의 DataSet의 Serialize된 객체의 사이즈를 알수 있겠습니다. 단순하게 Serialize하는 속도 및 Deserialize하는 속도는 같다고 가정을 합니다.
public class SerializeModel implements Serializable {
private static final long serialVersionUID = -7168303693593724718L;
private int count = 0;
private String name = null;
private String address = null;
private ArrayList<SerializeModel> models = new ArrayList<SerializeModel>();
public void AddModel(SerializeModel model) {
this.models.Add(model);
}
}
}
SerializeModelDataSet.cs
using System;
using System.Data;
using System.Collections.Generic;
using System.Text;
namespace SerialzieTest
{
[Serializable]
class SerializeModelDataSet
{
private DataSet dataSet = new DataSet();
public void AddDataTable(DataTable table) {
this.dataSet.Tables.Add(table);
}
public DataSet GetDataSet()
{
return this.dataSet;
}
}
}
Program.cs
using System;
using System.Data;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerialzieTest
{
class Program
{
static void SerializeObjectArray()
{
Stream stream = File.Open("csharp.ser", FileMode.Create);
BinaryFormatter bformat = new BinaryFormatter();
SerializeModel model = new SerializeModel(0, "a", "b");
for (int i = 0; i < 10; i++)
{
model.AddModel(new SerializeModel(i, i + " name", i + " address"));
}
bformat.Serialize(stream, model);
stream.Flush();
stream.Close();
}
static void SerializeDataSet()
{
Stream stream = File.Open("csharp_dataset.ser", FileMode.Create);
BinaryFormatter bformat = new BinaryFormatter();
SerializeModelDataSet model = new SerializeModelDataSet();
DataTable table = new DataTable();
DataColumn itemCount = new DataColumn("count", Type.GetType("System.Int32"));
DataColumn itemName = new DataColumn("name", Type.GetType("System.String"));
DataColumn itemAddress = new DataColumn("address", Type.GetType("System.String"));
네트웍을 통해서 전송되는 Data의 크기는 성능에 중요한 영향을 미치지요..
실제로 성능을 높이기 위해서 Socket Buffer의 사이즈를 적당히 줄이는 것도 팁으로 나와 있구요..
위 상황에서 C#의 DataSet은 더욱 멋진 모습을 보여줍니다.
아래의 화면처럼 말이죠.. 아래의 화면은 Hex Viewer로 본 화면입니다.
Object의 Serialize는 C#이 version, locale 및 가비지(?) 데이타로 인해서 좀 더 크게 나옵니다.
그리고, 중간의 DataSet은 Serialize된 내용에 xml의 형태로 저장을 하고 있네요.. 결국 DataSet이 데이타로 네트웍을 통해서 전송이 되면, 사이즈보다는 Serialize/Deserialize할때의 String연산(XML 데이타 리드)이 성능저하의 주범이 되지 않을까요? 결국, Tomcat, IIS가 중요한게 아니라 C#에서 널리 쓰이는 DataSet을 많이 사용하고 있는 IIS기반의 ASP.NET 어플들의 서비스가 더 성능이 안 좋을 거라는 생각이 드네요... ^^;;;;
java.util.Timer를 Logging 라이브러리에서 사용을 하다보니, Logging라이브러리를 사용하고 있는 로깅서버에서 addShutdownHook() 메쏘드를 통해서 등록된 쓰레드가 실행이 안 된다.. ^^;;
Java API 문서를 보면, 아래와 같은 내용이 있습니다.
Timer 객체의 마지막 라이브 참조가 종료해, 미처리의 태스크가 모두 실행되면, 타이머의 태스크 실행 thread도 동시에
완료해, 가비지 컬렉트됩니다. 다만, 이것에는 한없고 긴 시간이 걸리는 경우가 있습니다. 디폴트에서는 태스크의 실행 thread는 「demon
thread」로서는 실행되지 않기 때문에 어플리케이션이 종료하지 않게 할 수 있습니다. 타이머의 태스크 실행 thread를 즉시 완료시키는
경우, 호출측은 타이머의 cancel 메서드를 호출할 필요가 있습니다.
흠... 그렇군요.. 종료가 안되더군요... ^^;;
그래서, Timer() 대신 daemon으로 Timer객체를 생성하면, addShutdownHook() 메쏘드에 등록된 쓰레드의 run() 메쏘드가 잘 동작하더군요.. ^^;;
1. Always start your Timer as a daemon thread. By default, new Timer() and other constructors use a non-daemon thread which means that the Timer will stick around forever and prevent shutdown. That may be what you expect, but I doubt it.
2. Always name your Timer. That name is attached to the Timer background thread. That way, if you do something dumb, and look at a thread dump, it will be exceedingly obvious when you’ve started 500 FooBarCleanupThreads.
위의 내용을 요약하면, "가능한 데몬 쓰레드로 실행을 시키고 Timer에 이름을 줘서 쓰레드 덤프등을 통해서 분명하게 디버깅을 할 수 있도록 하자"로 파악을 하였습니다.
그리고, http://tech.puredanger.com 블로그에 있는 내용의 댓글로 Timer를 사용하지 말고, ScheduledExecutorService를 사용하는 것이 좋다고 하네요.. 이유는 Timer(싱글 쓰레드)가 가지는 원천적인 문제를 해결하기 위해서라고 하네요.. 원본 내용이 짧기 때문에 읽어보세요.
파일에 로그를 남긴다거나 데이타를 쓰기위해서 java.io.File 객체를 써도 되지만, 성능을 위해서 BufferedWriter를 많이 이용하게 됩니다. 특히 로그 라이브러리나 로깅 유틸리티 클래스에서 거의 대부분 사용할 것으로 생각이 드네요..
그래서, 생각한 방법이 BufferedWriter를 캐싱하는 것입니다.
아래 코드의 shutdown() 메쏘드는 ShudownHooking으로 등록된 쓰레드가 프로세스가 종료되면 호출을 해서 버퍼에 남아있는 내용을 기록하도록 되어 있습니다.
/** shutdown hooking thread call */
public static void shutdown() {
// 아래의 코드를 통해서 종료가 되더라도 다 flush 하고 종료한다.
Iterator<BufferedWriter> iter = null;
try {
iter = cache.values().iterator();
while (iter.hasNext()){
iter.next().close();
//iter.next().close();
}
지정한 태스크가 지정한 지연의 후에 시작되어 「고정 빈도 실행」을 반복하도록 스케줄 합니다. 그 후는 지정한 기간과는 별도로 거의 일정한 간격으로 실행됩니다.
Java API의 schedule 메쏘드에 대한 내용은,
고정 지연 실행에서는 전의 실행의 실제의 실행 시간을 기준으로의해 각각의 실행이 스케줄 됩니다. 어떠한 이유로써 실행이 지연 했을 경우 (가비지 컬렉션, 그 외의 백그라운드 작업 등), 그 후의 실행도 지연 됩니다. 최종적으로 실행의 빈도는 보통, 지정한 기간의 대응하는 빈도보다 약간 늦어집니다 (기본이 되는 Object.wait(long)를 지지하고 있는 시스템 클록이 정확이라고 하는 전제로).
Java API의 scheduleAtFixedRate 메쏘드에 대한 내용은,
고정 빈도 실행에서는 최초의 실행의 스케줄 된 실행 시간을 기준으로의해 각각의 실행이 스케줄 됩니다. 어떠한 이유로써 실행이 지연 했을 경우 (가비지 컬렉션 또는 그 외의 백그라운드 작업 등), 「지연을 되찾는다」위해 2개 이상의 실행이 연속해 행해집니다. 최종적으로 실행의 빈도는 지정한 기간의 대응하는 빈도와 같게 됩니다 (기본이 되는 Object.wait(long)를 지지하고 있는 시스템 클록이 정확이라고 하는 전제로).
따라서, 위 내용을 토대로 schedule되는 job의 수행 횟수 또는 비교적 critical한 수행을 해야 되는 경우에는 scheduleAtFixedRate 메쏘드를 사용해야 될것 같습니다.