월별 글 목록: 2008년 6월월

java charset에 대해서..

이기종의 플랫폼과 연동할 이슈가 발생하면, 네트웍을 통한 Byte Ordering과 더불어 스트링의 인코딩/디코딩도 이슈거리입니다.  아래는 자바 API 5의 java.nio.charset.Charset 에 나오는 내용입니다.

표준 캐릭터셋
Java 플랫폼의 구현은 모두 다음의 표준 캐릭터셋를 지원 할 필요가 있습니다. 지원 되고 있는 그 외의 캐릭터셋에 대해서는 구현의 릴리스 노트를 참조하십시오. 그러한 옵션의 캐릭터셋의 동작은 구현 마다 다를 가능성이 있습니다.

캐릭터셋           설명
US-ASCII          7 비트 ASCII (ISO646-US/Unicode 캐릭터셋의 Basic Latin 블록)
ISO-8859-1      ISO Latin Alphabet No. 1 (ISO-LATIN-1)
UTF-8                8 비트 UCS 변환 형식
UTF-16BE        16 비트 UCS 변환 형식, 빅 endian 바이트순서
UTF-16BE        16 비트 UCS 변환 형식, little endian 바이트순서
UTF-16             16 비트 UCS 변환 형식, 옵션의 바이트순서 마크로 식별되는 바이트순서

아래코드는 예제입니다.

class CharSetTest {

    static String aa = “a1”;

    static void testAscii() {
        try {
            byte[] bytes = aa.getBytes(“US-ASCII”);
            for(int i=0; i < bytes.length; i++) {
                System.out.print(bytes[i]);
            }

            System.out.println(“”);
        }catch(java.io.UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    static void testUTF8() {
        try {
            byte[] bytes = aa.getBytes(“UTF-8”);
            for(int i=0; i < bytes.length; i++) {
                System.out.print(bytes[i]);
            }

            System.out.println(“”);
        }catch(java.io.UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    static void testUTF16() {
        try {
            byte[] bytes = aa.getBytes(“UTF-16”);
            for(int i=0; i < bytes.length; i++) {
                System.out.print(bytes[i]);
            }

            System.out.println(“”);
        }catch(java.io.UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    static void testUTF16BE() {
        try {
            byte[] bytes = aa.getBytes(“UTF-16BE”);
            for(int i=0; i < bytes.length; i++) {
                System.out.print(bytes[i]);
            }

            System.out.println(“”);
        }catch(java.io.UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    static void testUTF16LE() {
        try {
            byte[] bytes = aa.getBytes(“UTF-16LE”);
            for(int i=0; i < bytes.length; i++) {
                System.out.print(bytes[i]);
            }

            System.out.println(“”);
        }catch(java.io.UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        System.out.println(“— Ascii —“);
        CharSetTest.testAscii();
        System.out.println(“— UTF-8 —“);
        CharSetTest.testUTF8();
        System.out.println(“— UTF-16 —“);
        CharSetTest.testUTF16();
        System.out.println(“— UTF-16BE —“);
        CharSetTest.testUTF16BE();
        System.out.println(“— UTF-16LE —“);
        CharSetTest.testUTF16LE();
    }
}

아래 예는 화면결과 입니다.

— Ascii —
9749
— UTF-8 —
9749
— UTF-16 —
-2-1097049
— UTF-16BE —
097049
— UTF-16LE —
970490

call ant task in another build file

Ant로 프로젝트를 빌드할 경우, 의존관계에 있는 프로젝트도 새롭게 빌드하기 위해서는,메인 빌드파일에서 다른 빌드파일의 task를 실행시켜 줘야 합니다.

<target name=”appc”>
  <ant antfile=”${build.dir}/ant/utilities.xml” inheritAll=”true” target=”appc”>
    <property name=”classpath” refid =”${appserver}.path”/>
    <property name=”appcMemory” value=”256m”/>
  </ant>
</target>

I/O 퍼포먼스 개선

자바에서는 자료를 읽거나 쓰기 위해 stream을 사용한다. 자바는 두가지 형태의 stream을 지원한다. Readers and Writers와 Input and Output stream이 그것이다. Reader and Writers는 high-level의 I/O(예. String)를 지원하고 Input and Output stream은 low-level의 I/O(byte)를 지원한다. 속도 향상을 위해서는 Buffered stream을 사용한다. Buffered stream을 사용할 경우 버퍼의 기본값은 2K이다. 이 값은 조정될 수 있으나, 자료의 용량이 클 경우 메모리가 많이 필요하기 때문에 Buffered stream을 사용할 경우 여러 가지 사항을 고려해야 한다.

예제1) Simple file copy

public static void copy(String from, String to) throws IOException{
  InputStream in=null;
  OutputStream out=null;
  try{
    in=new FileInputStream(from);
    out=new FileOutputStream(to);
    while(true){
        int data=in.read();
        if(data==-1)
          break;
        out.write(data);
    }
    in.close();
    out.close();
  }finally{
    if(in!=null){in.close();}
    if(out!=null){out.close();}
  }
}

Buffered stream을 사용하지 않고 I/O를 했을 경우의 예제이다. 370K의 JPEG 파일을 복사하는데 10800ms.

예제2) Faster file copy

public static void copy(String from, String to) throws IOException{
  InputStream in=null;
  OutputStream out=null;
  try{
    in=new BufferedInputStream(new FileInputStream(from));
    out=new BufferedOutputStream(new FileOutputStream(to));
    while(true){
      int data=in.read();
      if(data==-1)
        break;
      out.write(data);
    }
  }finally{
    if(in!=null){in.close();}
    if(out!=null){out.close();}
  }
}

Bufferd stream을 사용해서 퍼포먼스를 개선한 예제이다. 예제1)과 같은 파일을 복사하는데 130ms.

예제3) Custom buffered copy

public static void copy(String from, String to) throws IOException{
  InputStream in=null;
  OutputStream out=null;
  try{
    in=new FileInputStream(from);
    out=new FileOutputStream(to);
    int length=in.available();
    byte[] bytes=new byte[length];
    in.read(bytes);
    out.write(bytes);
  }finally{
    if(in!=null){in.close();}
    if(out!=null){out.close();}
  }
}

while 루프를 사용하지 않고 배열을 사용함으로서 퍼포먼스를 개선한 예제이다. 예제1)과 같은 파일을 복사하는데 33ms. 하지만 예제3)은 byte배열로 선언되는 메모리 버퍼의 크기가 실제 파일의 크기와 동일해야 한다. 이에 따라 두 가지의 문제점이 발생할 수 있다. 첫 번째는 파일의 용량이 클 경우 상당한 메모리 낭비를 초래한다는 점이다. 두 번째 문제점은 copy()메소드가 수행될 때마다 new byte[]에 의해 버퍼가 새로 만들어진다는 점이다. 만일 파일의 용량이 클 경우 버퍼가 만들어지고 Garbage Collector에 의해 수집될 때 상당한 OverHead를 초래할 수 있다.

예제4) Improved custom buffered copy

static final int BUFF_SIZE=100000;
static fianl byte[] buffer=new byte[BUFF_SIZE];
public static void copy(String from, String to) throws IOException{
  InputStream in=null;
  OutputStream out=null;
  try{
    in=new FileInputStream(from);
    out=new FileOutputStream(to);
    while(true){
      synchronized(buffer){
        int amountRead=in.read(buffer);
        if(amountRead==-1)
          break;
        out.write(buffer,0,amountRead);
      }
  }finally{
    if(in!=null){in.close();}
    if(out!=null){out.close();}
  }
}

크 기가 100K인 byte 배열을 임시버퍼로 지정하고, 이를 static으로 선언함으로서 퍼포먼스를 개선했다. 예제1)과 같은 파일을 복사하는데 22ms. static buffer의 사용으로 I/O작업을 수행할 경우 발생할 수 있는 문제점을 해결하기 위해 synchronized block을 사용했다. 비록 synchronization을 사용함에 따라 성능 저하를 초래하지만, 실제 while 루프에 머무는 시간이 극히 짧기 때문에 퍼포먼스에 문제는 없다. 테스트에 의하면 synchronized 버전과 unsynchronized 버전 모두 같은 시간에 수행을 완료했다.

Object 재사용

Object 재사용(메소드를 사용한 Object 초기화)
예제1)

StringBuffer sb=new StringBuffer();
sb.append(“Hello”);
out.println(sb.toString());
sb=null;
sb=new StringBuffer();
sb.append(“World”);
out.println(sb.toString());

위의, 예제1)과 같이 사용할 경우 하나의 인스턴스 변수를 사용하기는 하지만, 두 번의 초기화 과정을 거치게 된다.

예제2)

StringBuffer sb=new StringBuffer();
sb.append(“Hello”);
out.println(sb.toString());
sb.setLength(0);
sb.append(“World”);
out.println(sb.toString());

위와 같이 각 클래스에서 지원해 주는 메소드를 사용하여 Object를 재사용 할 수 있다.

Avoiding Garbage Collection

Avoiding Garbage Collection(static method사용)
예제1)

String string=”55″;
int theInt=new Integer(string).intValue();

예제2)

String string=”55″;
int theInt=Integer.parseInt(string);

예제1)에서는 Integer 클래스를 생성한 다음 string에서 정수값을 추출해 냈다. Object를 생성하고 초기화하는 과정은 상당한 cpu 집약적인 작업이고, Heap 영역에 Object가 생성되고 수집되기 때문에 가비지 컬렉터 작업을 수반한다. 따라서, 예제2)처럼 Object의 Instance가 필요없는 static 메소드를 사용한다.

Avoiding Garbage Collection(임시 Object 생성 금지)
가장 흔한 예가 String  객체의 + 연산자를 사용하는 것이다. + 연산자를 사용해서 String 객체를 append할 경우 우리가 생각하는 것 보다 훨씬 더 많은 임시 Object가 생성되고, 가비지 컬렉터에 의해 다시 수집된다. 따라서, String 보다 StringBuffer를 사용해라.

예제1)

String a=”Hello”;
a=a+”World”;
System.out.println(a);

예제2)

StringBuffer a=new StringBuffer();
a.append(“Hello”);
a.append(“World”);
System.out.println(a.toString());

어떤 메소드는 Object의 복사본을 반환하는 경우가 있다. 대표적인 예로 스트링 클래스의 trim() 메소드를 들 수 있다. trim()메소드가 수행되면 기존의 Object는 수집되고, 기존 Object의 복사본이 사용되게 된다. 임시 Object 생성과 복사본을 반환하는 메소드가 루프안에서 사용될 경우 무수히 많은 Object가 생성되고 수집되기 때문에 심각한 문제를 야기하게 된다. 루프 내부에서 Object를 생성하는 것은 가급적 피해야 한다.

예제3)

for(i=0;i<1000;i++){
Date a=new Date();
:
}

예제4)

Date a;
for(i=0;i<1000;i++){
a=new Date();
:
a=null;
}

예 제3)과 예제4)는 현재 날짜와 시간을 얻어오기 위해 루프 안에서 Object를 생성했다. 보통 set으로 시작하는 메소드는 Object의 Instance 값을 재정의 한다. API를 충분히 참조한 다음 지원 메소드가 없을 경우, 클래스를 상속받아 요구에 적합한 메소드를 만드는 방법도 고려할 필요가 있다.

Avoiding Garbage Collection(primitive data type 사용)
Date 클래스나 String 클래스의 값들중 int나 long 형으로 표현하여 사용할 수 있는 경우가 있다. 예를 들어

String a=”1492″;
String b=”1997″;

과 같을 경우 a와 b는 굳이 String 형으로 표현하지 않아도 된다. 하지만 여기서 주의할 점은 Object의 값을 기본 데이타형으로 Casting 하는 작업이 오히려 시간이 더 많이 걸릴 수도 있는 것이다. 클래스의 인스턴스 변수로 기본 데이타형을 사용하면 Object의 크기도 줄어들고, Object 생성 시간도 줄일 수 있다.