달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2009. 4. 15. 16:20

JVM(Java Virtual Machine) 성능 조정 Enjoy/JAVA2009. 4. 15. 16:20




 Application Server - Express, 버전 6.0.x
             운영 체제: AIX, HP-UX, Linux, Solaris, Windows

             목차 및 검색 결과 개인화

JVM(Java Virtual Machine) 성능 조정


출처: http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tprf_tunejvm.html

Java 프로세스인 Application Server는 실행하고 서버에서 실행하는 Java 응용프로그램을 지원하기 위해 JVM(Java Virtual Machine)이 필요합니다. Application Server 구성의 일부로서 JVM의 시스템 사용을 개선시키는 설정을 미세 조정할 수 있습니다.

이 타스크 정보

JVM은 Java 기반 응용프로그램을 위한 런타임 실행 환경을 제공합니다. WebSphere Application Server는 JVM 런타임 환경과 Java 기반 서버 런타임의 조합입니다. 다른 JVM 프로바이더의 JVM에서 실행할 수 있습니다. 사용자의 Application Server가 실행 중인 JVM 프로바이더를 판별하려면 WebSphere Application Server app_server_root/java/bin 디렉토리에서 java –fullversion 명령을 발행하십시오. 또한 서버 중 하나에서 SystemOut.log를 검사할 수도 있습니다. Application Server가 시작할 때 WebSphere Application Server는 JVM 프로바이더 정보를 포함한 JVM에 관한 정보를 이 로그 파일에 기록합니다.

JVM 조정 Perspective에서 두 가지 기본 유형의 JVM이 있습니다.
  • IBM JVM
  • Solaris의 Sun HotSpot JVM 및 HP의 HP-UX용 JVM을 포함한 Sun HotSpot 기반 JVM
JVM 조정이 JVM 프로바이더에 따라 다르지만 일반 조정 개념은 모든 JVM에 적용됩니다. 일반 개념은 다음을 포함합니다.
  • 컴파일러 조정. 모든 JVM은 JIT(Just In Time) 컴파일러를 사용하여 서버 런타임 중에 Java 바이트 코드를 기본 명령어로 컴파일합니다.
  • Java 메모리 또는 힙 조정. JVM 메모리 관리 기능 또는 가비지 콜렉션이 JVM 성능 향상을 위한 가장 큰 기회 중 하나를 제공합니다.
  • 클래스 로딩 조정.

프로시저

  • 시작 성능 및 런타임 성능을 최적화하십시오.

    일부 환경에서는 런타임 성능보다 WebSphere Application Server의 시작 성능을 최적화하는 것이 더 중요합니다. 다른 환경에서는 런타임 환경을 최적화하는 것이 더 중요합니다. 기본적으로 IBM JVM은 런타임 환경을 위해 최적화되는 반면 HotSpot 기반 JVM은 시작 성능을 위해 최적화됩니다.

    Java JIT 컴파일러는 시작 또는 런타임 성능이 최적화되는지 여부에 큰 영향을 줍니다. 컴파일러가 사용하는 초기 최적화 레벨은 클래스 메소드를 컴파일하는 데 걸리는 시간과 서버를 시작하는 데 걸리는 시간에 영향을 줍니다. 더 빠른 시작을 위해 컴파일러가 사용하는 초기 최적화 레벨을 줄일 수 있습니다. 이는 클래스 메소드가 이제 더 낮은 최적화 레벨에서 컴파일되기 때문에 응용프로그램의 런타임 성능이 저하될 수 있음을 의미합니다.

    재컴파일이 더 좋은 성능을 제공할 수 있다는 컴파일러의 판단에 따라서 컴파일러가 런타임 실행 중에 클래스 메소드를 다시 컴파일할 수 있기 때문에 특정한 런타임 성능 영향 명령문을 규정하기는 어렵습니다. 궁극적으로 응용프로그램의 지속 기간이 발생하는 런타임 저하의 양에 대한 주요 영향입니다. 짧게 실행하는 응용프로그램은 메소드가 다시 컴파일되는 확률이 더 높습니다. 장기간 실행하는 응용프로그램은 메소드가 다시 컴파일될 가능성이 작습니다. IBM JVM의 기본 설정은 초기 컴파일에 대해 높은 최적화 레벨을 사용합니다. 이 동작을 변경해야 하는 경우 다음 IBM JVM 옵션을 사용할 수 있습니다.

    -Xquickstart

    이 설정은 IBM JVM이 클래스 메소드 컴파일에 대해 더 낮은 최적화 레벨을 사용하는 방법에 영향을 주며, 이는 런타임 성능을 희생하는 대신 더 빠른 서버 시작을 제공합니다. 이 매개변수가 지정되지 않는 경우 IBM JVM은 기본적으로 컴파일에 대해 높은 초기 최적화 레벨로 시작합니다. 이 설정은 서버 시작은 더 느린 대신 더 빠른 런타임 성능을 제공합니다.

    기본값: 높은 초기 컴파일러 최적화 레벨
    권장: 높은 초기 컴파일러 최적화 레벨
    사용법: -Xquickstart는 더 빠른 서버 시작 시간을 제공할 수 있습니다.

    Sun의 Hotspot 기술을 기초로 하는 JVM은 초기에 낮은 최적화 레벨에서 클래스 메소드를 컴파일합니다. 다음 JVM 옵션을 사용하여 이 동작을 변경하십시오.

    -server

    Sun의 Hotspot 기술을 기초로 하는 JVM은 초기에 낮은 최적화 레벨에서 클래스 메소드를 컴파일합니다. 이들 JVM은 단순 컴파일러 및 최적화 JIT 컴파일러를 사용합니다. 일반적으로 단순 JIT 컴파일러가 사용됩니다. 그러나 이 옵션을 사용하여 최적화 컴파일러를 사용되는 컴파일러로 만들 수 있습니다. 이 변경은 서버의 성능을 크게 증가시키지만 최적화 컴파일러가 사용될 때 서버가 시작하는 데 더 오래 걸립니다.

    기본값: 단순 컴파일러
    권장: 최적화 컴파일러
    사용법: -server가 최적화 컴파일러를 사용 가능하게 합니다.
  • 힙 크기를 설정하십시오. 다음 명령행 매개변수가 힙 크기 설정에 유용합니다.
    • -Xms

      이 설정은 Java 힙의 초기 크기를 제어합니다. 이 매개변수를 적절하게 조정하면 가비지 콜렉션의 오버헤드를 줄여서 서버 응답 시간 및 처리량을 개선합니다. 일부 응용프로그램의 경우, 이 옵션에 대한 기본 설정이 너무 낮아서 사소한 가비지 콜렉션의 수가 높아질 수 있습니다.

      기본값: 256 MB
      권장: 워크로드에 특정하지만, 기본값보다 높습니다.
      사용법: -Xms256m은 초기 힙 크기를 256MB로 설정합니다.
    • -Xmx

      이 설정은 Java 힙의 최대 크기를 제어합니다. 이 매개변수를 적절하게 조정하면 가비지 콜렉션의 오버헤드를 줄여서 서버 응답 시간 및 처리량을 개선할 수 있습니다. 일부 응용프로그램의 경우, 이 옵션에 대한 기본 설정이 너무 낮아서 사소한 가비지 콜렉션의 수가 높아질 수 있습니다.

      기본값: 512 MB
      권장: 워크로드에 특정하지만, 기본값보다 높습니다.
      사용법: -Xmx512m은 최대 힙 크기를 512MB로 설정합니다.
    • -Xlp

      이 설정은 IBM JVM과 함께 사용되어 대형 페이지를 사용하는 힙을 할당할 수 있습니다. 그러나 이 설정을 사용하는 경우 운영 체제가 대형 페이지를 지원하도록 구성되어야 합니다. 대형 페이지를 사용하면 힙 메모리를 추적하기 위해 필요한 CPU 오버헤드를 줄일 수 있으며 또한 더 큰 힙의 작성을 허용할 수 있습니다.

      운영 체제 조정에 대한 자세한 정보 운영 체제 성능 조정 의 내용을 참조하십시오.

    사용자가 힙에 대해 지정해야 하는 크기는 시간에 따른 힙 사용량에 따라 다릅니다. 힙 크기가 자주 변경되는 경우에는 Xms 및 Xmx 매개변수에 동일한 값을 지정하는 경우 성능을 향상시킬 수 있습니다.

  • IBM JVM의 가비지 콜렉터를 조정하십시오.

    Java -X 옵션을 사용하여 메모리 옵션의 목록을 참조하십시오.

    • -Xgcpolicy

      gcpolicy를 optthruput으로 설정하면 동시 마크가 사용 불가능합니다. 오류가 있는 응용프로그램 응답 시간으로 표시되는 일시정지 시간 문제점이 있지 않은 경우 이 옵션을 사용하여 최상의 처리량을 가져와야 합니다. gcpolicy를 optavgpause로 설정하면 동시 표시가 기본값과 함께 사용 가능하게 됩니다. 이 설정은 정상 가비지 콜렉션에 의해 유발되는 오류가 있는 응용프로그램 응답 시간을 완화시킵니다. 그러나 이 옵션은 전체 처리량을 줄일 수 있습니다.

      기본값: optthruput
      권장: optthruput
      사용법: Xgcpolicy:optthruput
    • -Xnoclassgc

      기본적으로 JVM은 클래스의 활동하는 인스턴스가 없을 때 메모리에서 해당 클래스를 로드 해제하지만, 이는 성능을 저하시킬 수 있습니다. 클래스 가비지 콜렉션을 끄면 동일한 클래스를 여러 번 로드 및 로드 해제하는 오버헤드를 제거합니다.

      클래스가 더 이상 필요없는 경우 클래스가 힙에서 차지하는 공간은 일반적으로 새 오브젝트의 작성을 위해 사용됩니다. 그러나 클래스의 새 인스턴스를 작성하여 요청을 처리하는 응용프로그램이 있고 해당 응용프로그램에 대한 요청이 임의 시간에 오는 경우, 이전 요청자가 완료될 때 다음 요청이 나타날 때만 클래스를 다시 인스턴스화하기 위해 정상 클래스 가비지 콜렉션이 클래스가 차지했던 힙 공간을 사용 가능하게 해서 이 클래스를 지우는 것이 가능합니다. 이 상황에서는 이 옵션을 사용하여 클래스의 가비지 콜렉션을 사용하지 않을 수 있습니다.

      기본값: 클래스 가비지 콜렉션 사용 가능
      권장: 클래스 가비지 콜렉션 사용 불가능
      사용법: Xnoclassgc가 클래스 가비지 콜렉션을 사용 불가능하게 합니다.
    추가 정보에 대해서는 다음 DeveloperWorks 항목을 확인하십시오.
  •  Sun JVM의 가비지 콜렉터를 조정하십시오.

    Solaris 플랫폼에서 WebSphere Application Server는 IBM JVM이 아니라 Sun Hotspot JVM에서 실행합니다. 성능 최적화 기능을 이용하기 위해서는 Sun JVM과 함께 올바른 조정 매개변수를 사용하는 것이 중요합니다.

    Sun HotSpot JVM은 최적 성능을 달성하기 위해 세대별 가비지 콜렉션에 의존합니다. 다음 명령행 매개변수가 가비지 콜렉션 조정에 유용합니다.

    • -XX:SurvivorRatio

      Java 힙은 예전에 생성된(old) 즉, 오브젝트용 섹션과 최근에 생성된(young) 오브젝트용 섹션으로 나뉘어집니다. 최근에 생선된(young) 오브젝트용 섹션은 다시 새 오브젝트가 할당되는 섹션(eden)과 여전히 사용 중인 새 오브젝트가 이전 오브젝트로 승격되기 전에 처음 몇 번의 가비지 콜렉션에서 살아남는 섹션(감독자 공간)으로 나뉘어집니다. 감독자 비율은 힙의 최근에 생선된(young) 오브젝트 섹션에서 감독자 공간에 대한 eden의 비율입니다. 이 설정을 늘리면 높은 오브젝트 작성 및 낮은 오브젝트 보존을 갖는 응용프로그램에 대해 JVM을 최적화합니다. WebSphere Application Server가 다른 응용프로그램보다 더 많은 중간 및 오래 활동하는 오브젝트를 생성하므로, 이 설정은 기본값보다 낮아야 합니다.

      기본값: 32
      권장: 16
      사용법: -XX:SurvivorRatio=16
    • -XX:PermSize

      영구 생성을 위해 예약되는 힙의 섹션은 JVM에 대한 모든 반사 데이터를 보유합니다. 이 크기는 많은 클래스를 동적으로 로드하고 로드 해제하는 응용프로그램의 성능을 최적화하기 위해 늘려야 합니다. 이 설정을 값 128MB로 설정하면 힙의 이 파트를 증가시키는 오버헤드를 제거합니다.

      권장: 128 MB
      사용법: XX:PermSize=128m은 perm 크기를 128MB로 설정합니다.
    • -Xmn

      이 설정은 젋음 세대가 힙에서 소비하도록 허용되는 공간을 제어합니다. 이 매개변수를 적절하게 조정하면 가비지 콜렉션의 오버헤드를 줄여서 서버 응답 시간 및 처리량을 개선할 수 있습니다. 이 옵션에 대한 기본 설정은 일반적으로 너무 낮아서 사소한 가비지 콜렉션의 수를 높게 만듭니다. 이 설정을 너무 높게 설정하면 JVM이 주요(또는 전체) 가비지 콜렉션만 수행하게 만들 수 있습니다. 이들은 대개 몇 초가 걸리며 서버의 전체 성능에 극히 불리합니다. 이 상황을 피하기 위해 이 설정을 전체 힙 크기의 절반 이하로 유지해야 합니다.

      기본값: 2228224 바이트
      권장: 대략 총 힙 크기의 1/4
      사용법: -Xmn256m은 크기를 256MB로 설정합니다.
    • -Xnoclassgc

      기본적으로 JVM은 클래스의 활동하는 인스턴스가 없을 때 메모리에서 해당 클래스를 로드 해제하지만, 이는 성능을 저하시킬 수 있습니다. 클래스 가비지 콜렉션을 끄면 동일한 클래스를 여러 번 로드 및 로드 해제하는 오버헤드를 제거합니다.

      클래스가 더 이상 필요없는 경우 클래스가 힙에서 차지하는 공간은 일반적으로 새 오브젝트의 작성을 위해 사용됩니다. 그러나 클래스의 새 인스턴스를 작성하여 요청을 처리하는 응용프로그램이 있고 해당 응용프로그램에 대한 요청이 임의 시간에 오는 경우, 이전 요청자가 완료될 때 다음 요청이 나타날 때만 클래스를 다시 인스턴스화하기 위해 정상 클래스 가비지 콜렉션이 클래스가 차지했던 힙 공간을 사용 가능하게 해서 이 클래스를 지우는 것이 가능합니다. 이 상황에서는 이 옵션을 사용하여 클래스의 가비지 콜렉션을 사용하지 않을 수 있습니다.

      기본값: 클래스 가비지 콜렉션 사용 가능
      권장: 클래스 가비지 콜렉션 사용 불가능
      사용법: Xnoclassgc가 클래스 가비지 콜렉션을 사용 불가능하게 합니다.

    Sun JVM 조정에 대한 추가 정보는 Performance Documentation for the Java HotSpot VM를 확인하십시오.

  •  HP JVM의 가비지 콜렉터를 조정하십시오.

    HP JVM은 최적 성능을 달성하기 위해 세대별 가비지 콜렉션에 의존합니다. 다음 명령행 매개변수가 가비지 콜렉션 조정에 유용합니다.

    • -Xoptgc

      이 설정은 수명이 짧은 많은 오브젝트를 갖는 응용프로그램에 대해 JVM을 최적화합니다. 이 매개변수가 지정되지 않으면 JVM은 대개 주요(전체) 가비지 콜렉션을 수행합니다. 전체 가비지 콜렉션은 수 초가 걸릴 수 있으며 서버 성능을 크게 저하시킬 수 있습니다.

      기본값: off
      권장: on
      사용법: -Xoptgc는 최적화된 가비지 콜렉션을 사용 가능하게 합니다.
    • -XX:SurvivorRatio

      Java 힙은 예전에 생성된(old) 즉, 오브젝트용 섹션과 최근에 생성된(young) 오브젝트용 섹션으로 나뉘어집니다. 최근에 생선된(young) 오브젝트용 섹션은 다시 새 오브젝트가 할당되는 섹션(eden)과 여전히 사용 중인 새 오브젝트가 이전 오브젝트로 승격되기 전에 처음 몇 번의 가비지 콜렉션에서 살아남는 섹션(감독자 공간)으로 나뉘어집니다. 감독자 비율은 힙의 최근에 생선된(young) 오브젝트 섹션에서 감독자 공간에 대한 eden의 비율입니다. 이 설정을 늘리면 높은 오브젝트 작성 및 낮은 오브젝트 보존을 갖는 응용프로그램에 대해 JVM을 최적화합니다. WebSphere Application Server가 다른 응용프로그램보다 더 많은 중간 및 오래 활동하는 오브젝트를 생성하므로, 이 설정은 기본값보다 낮아야 합니다.

      기본값: 32
      권장: 16
      사용법: -XX:SurvivorRatio=16
    • -XX:PermSize

      영구 생성을 위해 예약되는 힙의 섹션은 JVM에 대한 모든 반사 데이터를 보유합니다. 이 크기는 많은 클래스를 동적으로 로드하고 로드 해제하는 응용프로그램의 성능을 최적화하기 위해 늘려야 합니다. 128MB의 값을 지정하면 힙의 이 파트를 증가시키는 오버헤드를 제거합니다.

      기본값: 0
      권장: 128MB
      사용법: -XX:PermSize=128m은 PermSize를 128MB로 설정합니다.
    • -XX:+ForceMmapReserved

      기본적으로 Java 힙은 "지연 스왑" 할당됩니다. 이는 필요할 때 메모리 페이지를 할당하여 스왑 공간을 절약하지만 또한 4KB 페이지의 사용을 강제합니다. 이 메모리 할당은 대형 힙 시스템에서 힙을 수십만개의 페이지에 분산시킬 수 있습니다. 이 명령은 "지연 스왑"을 사용 불가능하게 하고 운영 체제가 더 큰 메모리 페이지를 사용할 수 있게 하여 Java 힙을 구성하는 메모리에 대한 액세스를 최적화합니다.

      기본값: off
      권장: on
      사용법: -XX:+ForceMmapReserved는 "지연 스왑"을 사용 안합니다.
    • -Xmn

      이 설정은 젋음 세대가 힙에서 소비하도록 허용되는 공간을 제어합니다. 이 매개변수를 적절하게 조정하면 가비지 콜렉션의 오버헤드를 줄여서 서버 응답 시간 및 처리량을 개선할 수 있습니다. 이 옵션에 대한 기본 설정은 일반적으로 너무 낮아서 사소한 가비지 콜렉션의 수를 높게 만듭니다.

      기본값: 기본값 없음
      권장: 대략 총 힙 크기의 3/4
      사용법: -Xmn768m은 크기를 768MB로 설정합니다.
    • 가상 페이지 크기

      JVM(Java Virtual Machine) 명령어 및 데이터 페이지 크기를 64MB로 설정하면 성능이 향상될 수 있습니다.

      기본값: 4MB
      권장: 64MB
      사용법: 다음 명령을 사용하십시오. 명령 출력이 프로세스 실행 파일의 현재 운영 체제 특성을 제공합니다.
      chatr +pi64M +pd64M /opt/WebSphere/
      AppServer/java/bin/PA_RISC2.0/
      native_threads/java
    • -Xnoclassgc

      기본적으로 JVM은 클래스의 활동하는 인스턴스가 없을 때 메모리에서 해당 클래스를 로드 해제하지만, 이는 성능을 저하시킬 수 있습니다. 클래스 가비지 콜렉션을 끄면 동일한 클래스를 여러 번 로드 및 로드 해제하는 오버헤드를 제거합니다.

      클래스가 더 이상 필요없는 경우 클래스가 힙에서 차지하는 공간은 일반적으로 새 오브젝트의 작성을 위해 사용됩니다. 그러나 클래스의 새 인스턴스를 작성하여 요청을 처리하는 응용프로그램이 있고 해당 응용프로그램에 대한 요청이 임의 시간에 오는 경우, 이전 요청자가 완료될 때 다음 요청이 나타날 때만 클래스를 다시 인스턴스화하기 위해 정상 클래스 가비지 콜렉션이 클래스가 차지했던 힙 공간을 사용 가능하게 해서 이 클래스를 지우는 것이 가능합니다. 이 상황에서는 이 옵션을 사용하여 클래스의 가비지 콜렉션을 사용하지 않을 수 있습니다.

      기본값: 클래스 가비지 콜렉션 사용 가능
      권장: 클래스 가비지 콜렉션 사용 불가능
      사용법: Xnoclassgc가 클래스 가비지 콜렉션을 사용 불가능하게 합니다.

    HP 가상 시스템 조정에 대한 추가 정보는 Java technology software HP-UX 11i를 확인하십시오.

  •  HP-UX를 위해 HP의 JVM을 조정하십시오. 다음 옵션을 설정하여 응용프로그램 성능을 개선하십시오.
    -XX:SchedulerPriorityRange=SCHED_NOAGE 
    -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.DevPollSelectorProvider
    -XX:-ExtraPollBeforeRead
  • 특정 상황에서 수행되는 덤프 수를 제한하십시오.

    특정 오류 상황에서는 복수 Application Server 스레드가 실패하여 JVM이 해당 스레드 각각에 TDUMP를 요청합니다. 이러한 경우 많은 수의 TDUMP가 동시에 수행되어 보조 기억장치 부족과 같은 다른 문제점이 발생할 수 있습니다. JAVA_DUMP_OPTS 환경 변수를 사용하여 특정 상황에서 JVM이 생성할 덤프 수를 표시할 수 있습니다. 그러나 Application Server에서 실행 중인 응용프로그램의 com.ibm.jvm.Dump.SystemDump() 호출로 인해 생성되는 TDUMPS 수에는 영향을 주지 않습니다.

    예를 들어, JAVA_DUMP_OPTS 변수를 다음 옵션과 함께 지정하는 경우 JVM의 역할은 다음과 같습니다.
    • TDUMP 토큰 수를 1로 제한합니다.
    • JAVADUMP 토큰 수를 최대값 3으로 제한합니다.
    • INTERRUPT가 발생하는 경우 문서를 캡처하지 않습니다.
    JAVA_DUMP_OPTS=ONANYSIGNAL(JAVADUMP[3],SYSDUMP[1]),ONINTERRUPT(NONE) 

    JAVA_DUMP_OPTS 환경 변수 사용에 대한 자세한 정보는 IBM 개발자 킷 진단 안내서를 참조하십시오.

다음에 수행할 내용

추가 조정 정보는 Java 메모리 성능 조정 팁 의 내용을 참조하십시오.

:
Posted by 라면스프
2009. 4. 15. 16:17

finalize method란 무엇인가? Enjoy/JAVA2009. 4. 15. 16:17



finalize method란 무엇인가?

최상위 클래스인 Object클래스는 finalize라는 이름의 protected 메쏘드를 가지고 있다. 이것은 이 클래스의 객체가 가비지 콜렉션 과정에서 가지고 있던 리소스가 반환될 때 해야 할 일에 대한 정의를 가지고 있다. 물론, 이것은 사용자가 정의한 클래스에서 재정의할 수 있다. 만일 사용자의 클래스에 이 메쏘드를 재정의했다면, 그 finalize메쏘드를 객체의 Finalizer라고 부른다. 그렇다면 이것은 누가 호출하는가? 자바 가상 머신이 그 객체가 가지고 있는 메모리가 반환되기 전에 이것을 수행한다. 이 메쏘드를 재정의함으로써, 단순히 메모리를 반환하는 것 이상의 작업을 할 수 있다.  

 

여기에 대한 정의는 자바 가상 머신에 되어 있지 않다. 단순히 그 객체가 사용하고 있던 리소스가 재사용되기 전에만 실행되면 된다. 어느 쓰레드가 이 파이널라이저를 실행해야 한다는 규정도 없다. Object 클래스의 finalizer메쏘드가 하는 일은 아무 것도 없다.

또한 하위 클래스의 파이널라이저는 상위 클래스의 파이널라이저를 암시적으로 호출하지 않는다. 생성자와는 다르다. 생성자는 자신의 상위 클래스 객체의 생성자를 암시적으로 호출하게 된다. 그래서 하위 클래스의 파이널라이저는 항상 super키워드를 통해 상위 클래스의 파이널라이저를 호출하는 것이 좋다.

protected void finalize() throws Throwable {
super.finalize(); .....
}

파이널라이저의 구현

이 개념을 잡기 위해 두 종류의 애트리뷰트들을 규정하는 것이 좋겠다.

Reachability

 

  • reachable : 살아 있는 쓰레드들에 의해 지속적으로 접근될 수 있는 객체의 상태. 프로그램의 최적화는 이런 reachable한 객체의 숫자를 줄이는 방향으로 나갈 수 있다. 예를 들어, 사용자가 명시적으로 하지 않았더라도 컴파일러나 코드 생성기가 더 이상 사용되지 않을 객체의 레퍼런스에 대해 미리 null이라고 세팅함으로써, 빠른 시간 내에 가비지 수집이 될 수 있게 하는 것이다.
  • finalizer-reachable : 현재 살아있는 쓰레드들에 의해 참조되지는 않으나, finalizable한 객체들에 의해 참조되고 있는 객체의 상태.
  • unreachable : 어떠한 객체들로부터도 참조되지 않는 객체.
Finalization
 
  • unfinalized : 파이널라이저가 자동으로 수행되지 않은 객체의 상태.
  • finalizable :  파이널라이저가 자동으로 수행되지 않은 상태이지만, 자바 가상 머신이 언제라도 파이널라이저를 수행할 수 있는 객체의 상태. 즉 파이널라이저에 의해서는 참조가 되고 있는 상태
  • finalized : 파이널라이저가 자동으로 수행된 객체의 상태.

  이와 같은 두 애트리뷰트의 부류를 고려한 객체의 라이프 사이클은 다음과 같다.

 객체의 라이프 사이클

  • A : 객체가 처음 생성되었을 때에는 reachable이면서 unfinalized한 상태이다
  • 객체의 참조가 실행 도중 살아 있는 쓰레드로부터 접근할 수 없게 되면서 객체의 상태는 finalizer-reachable(B,C,D)나 unreachable(E,F)의 상태가 된다.
  • 이 그림에서 주의깊게 봐야 할 것은 f-reachable한 상태에서 직접 unreachable한 상태로 이동하지는 않는다는 것이다. 파이널라이저가 수행시작되는 순간 reachable한 상태가 되면서 unreachable상태로 이동하게 되는 것이다.
  • G,H : unfinalized상태의 객체가 f-reachable이나 unreachable상태가 된 것을 자바 가상 머신이 탐지하고 finalizable이라는 상태로 세팅한다. 그리고, 그 객체가 unreachable 상태라면 f-reachable상태로 바꾸어 파이널라이저가 수행될 수 있도록 한다.
  • I : 파이널라이저가 수행되었고 unreachable이면 이 객체는 메모리로부터 제거된다.
  • J,K : 자바 가상 머신은 finalizable한 객체에 대해서는 임의의 쓰레드를 이용해 파이널라이저를 수행시킬 수 있다. 이 경우에 객체는 finalized이면서 reachable한 상태가 된다.
  • L,M,N : 위와 같은 작업에 의해 f-reachable이었던 다른 객체들의 상태가 reachable해지게 될 수도 있다.
  • 위에서 한 가지 상태가 빠져 있다. finalizable이면서 unreachable한 상태로 이런 상태는 있을 수 없다. finaliable한 객체는 파이널라이저에 의해 접근될 수 있기 때문이다.
  • O : 파이널라이저가 정의되지 않은 사용자 클래스의 경우 굳이 복잡한 상태를 거칠 이유가 없다.
강제 가비지 콜렉션 구현의 예
 
 import java.lang.ref.*;

public class FinalizeTest {
    String oops = "Oopsla";
    static Reference weakref;

          protected void finalize() throws Throwable {
          System.out.println("Finalizer Called");
}     public static void main(String[] args) {
     FinalizeTest f = new FinalizeTest();
          Reference weakref = new WeakReference(f);

                    System.out.println("Before GC = " + weakref.get());
                     f = null;
                    Runtime rt = Runtime.getRuntime();
                    long free = rt.freeMemory();
         long oldfree;

                 do {
oldfree = free;
rt.gc();
free = rt.freeMemory();
         } while (free > oldfree);

                    System.out.println("After GC = " + weakref.get());
}
}

실행 결과
Before GC = FinalizeTest@7923b8c9
Finalizer Called
AfterGC = null

클래스 파이널라이저

  클래스가 언로딩될 때 암시적으로 호출되는 메쏘드를 다음과 같이 정의할 수 있다.

static void classFinalize() throws Throwable { . . . }
:
Posted by 라면스프
2009. 4. 11. 11:56

Java garbage collection Enjoy/JAVA2009. 4. 11. 11:56




Java garbage collection

Qualcomm 부스에서 기다렸더니 옆집 아저씨같은 분이 반갑게 맞이하며 두 개 질문 하더라. vTable이 뭐야? 하하하 이건 우리 인터뷰 과외전문 art.oriented 김선생이 귀뜸해준 것이 아닌가? 상속받은 클래스가 여러 개 있는데 virtual 키워드를 쓰게 되면 실제 호출할 때 엉뚱한 메소드를 호출할 수도 있다. 그 때를 위해 메소드들에 대한 포인터를 가지고 있는 것이 vTable이라고 영어로 버벅거리며 대답을 했다. 내 대답에 아저씨는 미심쩍은 표정을 지어주셨다.

두 번째 질문은 Java에서 언제 garbage collection을 하는 가였다. gc라고 garbage collector가 있지만 그걸 내가 실행시킨다고 바로 garbege collector가 작동하는 것도 아니고 JVM이 알아서 자동으로 하는 거라고 말해줬다. 그랬더니 그럼 자동으로 어떻게 하냐고 되묻는다. 퀄컴 오피스가 학교 근처에 있길래 편하게 인턴하겠다 싶어서 지원했는데 레주메 내는 것조차 쉽지가 않다. 그래도 대답을 해야겠기에 일반적인 이야기를 했다. 자바가 자동으로 memory를 관리하고 reference counter등을 가지고 있어서 메모리가 부족하면 garbage collection이 일어난다고. 아저씨 표정은 아까보다 더 않 좋게 변했다. 인터뷰 하나가 날아가는 소리가 들린다. 

시험 볼 때 틀린 문제는 제대로 알아놓지 않으면 나중에 나왔을 때 또 틀리는 법! 그래서 잊어버리기 전에 한 번 적어보자. 구글신 가라사대 

The JVM specification says only that the heap of the Java virtual machine must be garbage collected. The specification does not define how the garbage collector must work. The designer of each JVM must decide how to implement the garbage-collected heap.

즉, 정해진 건 없다는 건데. 그럼 내가 말한 것처럼 레퍼런스 카운팅을 해서 구현해도 사실 문제가 없는 거잖아? 그 아저씨는 왜 이상한 표정을 지었을까. 그런데 저 위에 나온 문서는 96년에 Bill Venners라는 사람이 쓴 글이다. 그럼 좀 더 최신 문서를 찾아보면?

Last month, we looked at the classic garbage collection techniques of reference counting, copying, mark-sweep, and mark-compact. Each of these approaches has advantages and disadvantages in certain situations. For example, copying does well when a large proportion of objects are garbage, but does poorly with many long-lived objects (copying them repeatedly). Conversely, mark-compact does quite well with long-lived objects (copying them only once), but not so well with many short-lived objects. The technique used by the 1.2 and later JVMs, calledgenerational garbage collection, combines these two techniques to get the best of both worlds, and as a bonus provides very low object allocation overhead.

한 줄 요약하면, JVM 1.2이상 버전에서는 copying과 mark-compact 기술을 합친 generational garbage collection을 한다는 말이다. 내가 그 아저씨한테 reference counting을 한다고 했으니 아저씨 표정이 이랬지.


copying GC

블로그에 보니 알기 쉽게 설명이 잘 되어있다. 역시 나같은 사람은 그림을 보여줘야 이해가 빠르다. 간단하게 설명하자면 메모리를 두 개로 나눈다. 두 공간을 각각 ping과 pong으로 부르자. 처음에는 ping에만 stack처럼 메모리를 할당하고 stack의 top 포인터를 가장 마지막에 할당한 메모리를 가리키게 한다. 근데 계속 새로운 메모리를 할당하게 되면 ping이 꽉 차서 더 이상 메모리를 할당할 수 없게 된다. 그럼 그 때 GC를 실행시킨다. GC는 간단하다. pong에다가 현재 참조되고 있는 메모리를 다 옮겨놓는다. 그리고 ping은 다 지운다. 그리곤 pong이 다 찰 때까지 새로운 메모리는 pong에다가 계속 할당한다. 

이 방법은 매우 빠르다는 장점이 있다. 그냥 top 포인터는 계속 증가하기만하면 되고, 한 쪽이 다 차면 다른 한쪽으로 옮기면 끝. GC를 할 시점인지 아닌지도 top 포인터만 체크하면 된다. 자 이제 단점 들어간다. 항상 두 배의 메모리가 필요하다. ping과 pong 두 개를 유지해서 하나가 꽉 차면 다른 한쪽으로 옮겨야하기 때문에 나머지 하나는 항상 놀고 있다. 

mark-compact GC

이건 정말 짤막한 설명이어서 한 편으로는 머리속에 지식이 훅~ 하고 들어오지만, 한 편으로는 그림이 없어서 조금 애매하기도 하다. 알고리즘은 두 단계로 되어있다. 

  1. 모든 참조되는 object들에 mark를 해 놓는다.
  2. 마크된 것들은 contiguous memory location에다가 옮겨놓는다.
contiguous를 찾아보니 touching along a boundary, near in sequence, connected throughout in an unbroken sequence라는데 인접한 곳, 연속된 공간 뭐 이 정도로 해석해도 되려는지 모르겠다. 역시 그림이 없이 글로만 설명된 건 이해하기 어렵다.

generational garbage collection

A generational collector divides the heap into multiple generations.Objects are created in theyoung generation, and objects that meet some promotion criteria, such as having survived a certain number of collections, are then promoted to the next older generation.

쉽게 말해 메모리를 여러 세대(generation)으로 나누고 GC에서 살아 남거나 하면 다음 세대(next older generation)으로 승격되는 시스템 되겠다. JVM은 모든 메모리 영역에 대해서 GC를 하는 것이 아니라 메모리를 여러 영역으로 나누고, 해당 영역이 꽉 차면 그 영역에 대해서만 GC를 한다. 전체 영역에 대한 GC(Full GC)를 할 필요가 없고 일부 영역에 대한 GC(Minor GC)만 수행하므로 수행 속도가 빨라진다. 

Sun의 자바 사이트에서 가져온 그림

JVM은 메모리를 젊은 세대(Young Generation, 이하 YG)과 나이든 세대(Old Generation, 이하 OG)으로 구분한다. YG는 다시 Eden, 두 개의 Survivor 나뉜다. 처음 object가 생성이 되면 일단 Eden에 들어가게 되고 대부분의 object는 Eden에서 생을 마감하게 된다. Eden이 꽉 차면 Minor GC를 수행하고 살아 남는 object들은 Survivor 지역으로 이동된다. Survivor에서 어느 정도 오래 살아 남으면 Tenured 지역으로 이동되고, Tenured 지역이 꽉 차면 다시 Full GC가 일어난다. 

더 자세한 정보를 원한다면 링크1링크2를 보기 바란다.

:
Posted by 라면스프


[IBM Dump Analyzer] javacore 분석툴 jca 사용하기

jca 툴은 javacore 를 분석할 수 있는 툴입니다. 
덤프파일의 생성은 JVM이 죽을경우 코어 파일을 만들어지게 됩니다.
또는 사용자가 직접 kill -3 PID 명령어를 이용해 덤프 파일을 생성하시면 되겠죠.
이 툴은 ibm 에서 제공하구요. 참고로 heapdump 를 분석하는 툴로 HeapAnalyzer 가 있다고하네요.
이글은 툴 다운로드 부터 실행까지 입니다. 자세한건 사용하시면서 알아가시면 될듯합니다. ^^
코어 분석에 도움 참고 사이트로 J2EE 어플리케이션에서 Bottleneck의 발생과 대처 방법 가 있습니다.




jca 툴 다운로드 : http://www.alphaworks.ibm.com/tech/jca/download




다운로드시 로그인을 하셔야 합니다. ^^;  ibm ID가 없으시면 procees without an IBM ID 메뉴를 이용하시면 됩니다.



 asterisk (*)  체크된 부분만 입력하시고   I confirm 버튼 누르시면 됩니다.
참고로 View license 읽어보시구요. 한글도 지원하네요.

The fields indicated with an asterisk (*) are required to complete this transaction; other fields are optional. If you do not want to provide us with the required information, please use the "Back" button on your browser to return to the previous page, or close the window or browser session that is displaying this page.







jca31.zip 파일 다운로드가 완료 되시면 적당한 곳에 압축을 풀어주세요.

C:\jca31  전 여기에 풀었습니다.



툴 실행전에 사용자 환경에 자바가 설치 되어 있어야 합니다. ^^

경로 이동후 jar 파일을 실행 해주세요

java –Xmx300m –jar jca31.jar

경로 이동후 jar 파일을 실행 해주세요. 실행 때  -Xmx 옵션을 적용해서 메모리를 여유있게 주는것이 좋다고 하네요.




툴은 스윙으로 만들어져 있었습니다.

 Compre Threads 메뉴 이용해서 여러개의 javacore 파일을 비교 분석할 수도 있고




메모리상황 그리고 Thread의 Detail 한 정보까지 볼 수 있습니다.


참고로 readme31.zip 파일이 있습니다.

압축을 풀어보시면 readme.htm 파일이 있구요. 그걸 실행시키면

사용방법이 있습니다. 참고하시면 될듯합니다.   참고 : http://starplaying.tistory.com/81

 

:
Posted by 라면스프
2008. 11. 13. 21:00

IBM Thread and Monitor Dump Analyzer Enjoy/JAVA2008. 11. 13. 21:00


출처 : http://www.alphaworks.ibm.com/tech/jca
다운로드 : http://www.alphaworks.ibm.com/tech/jca/download

참고페이지 : http://starplaying.tistory.com/82

How to run IBM Thread and Monitor Dump Analyzer for Java

You need to use the Java 2 Platform, Standard Edition version 5.0 or higher Java Runtime Environment (JRE) to run IBM Thread and Monitor Dump Analyzer for Java.

Usage <Java path>java –Xmx[heapsize] –jar jca<Thread and Monitor Dump Analyzer for Java version>.jar [javacore files separated by path delimiter] [analysis output HTML file]

 

For example,

C:\java\bin\java –Xmx200m –jar jca13.jar

=> Starts IBM Thread and Monitor Dump Analyzer for Java version 1.3 and open javacore files from menu.

 

/usr/java/bin/java –Xmx200m –jar /usr/jca/jca13.jar /usr/javacore.20060228.171037.2084.txt;/usr/javacore.20060228.171048.2084.txt

=> Starts IBM Thread and Monitor Dump Analyzer for Java version 1.3 with /usr/javacore.20060228.171037.2084.txt and /usr/javacore.20060228.171048.2084.txt

 

java –Xmx500m –jar jca13.jar  /usr/javacore.20060228.171037.2084.txt;/usr/javacore.20060228.171048.2084.txt output.html (On Windows system)

=> Starts IBM Thread and Monitor Dump Analyzer for Java with /usr/javacore.20060228.171037.2084.txt and /usr/javacore.20060228.171048.2084.txt and generates analysis output in output.html without invoking graphical user interface

 

WARNING: If the analysis output HTML file exists, the tool will overwrite the file.

 

If there’s java.lang.OutOfMemoryError while you are processing Javacore files, please try increasing the maximum heap size (-Xmx) value to give the JVM more memory.

Maximum heap size should not be larger than the size of available physical memory size for this tool due to performance issue.

 

1. Start IBM Thread and Monitor Dump Analyzer for Java

 

 

The following screen is a snapshot of the initial screen.

 

 

 

2. Select File-> Open Thread Dumps to open Javacore files if you didn’t specify Javacore files in the command line during the startup.

  

3. You can select multiple Javacore files

 

 

 

 

4. Loaded Javacore files are listed in the Thread Dump List table.

 

 

 

  

Thread Dump List provides the following information:

  • Name: The file name of Javacore
  • Runnable/Total Threads : Number of runnable and total threads
  • Free/Allocated Heap : Amount of free and allocated Java heap
  • AF/GC Counter: Allocation Failure and Garbage Collector counter
  • Monitor : Number of Monitors locked. Deadlock is displayed if there’s any deadlock in Monitor

 

 

5. Click on a Javacore to display summary of the Javacore.

 

The following information is displayed for IBM Javacore:

 

  • File name
  • Cause of thread dump
  • Date
  • Process ID
  • Java version
  • Java Heap Information
    • Maximum Java heap size
    • Initial Java heap size
    • Garbage Collector Counter
    • Allocation Failure Counter
    • Free Java heap size
    • Allocated Java heap size
  • Current Thread Name
  • Number of loaded classes in Java heap
  • Recommended size of kCluster (Only applicable to IBM SDK 1.4.2 and 1.3.1 SR7 or later)
  • Java Command line

 

Non-IBM Javacore ( or thread dump ) provides limited information.

 

 

 

 

6. Click on Right Button of your mouse to bring up a menu.Or click on Analysis menu to display sub menu

 

 

 

7. The following is Thread Detail View of a Javacore:

 

Threads are sorted by thread name. Thread Detail View provides the following information:

 

  • Thread Name : The name of a thread
  • Thread State : The state of a thread for example, Runnable, Waiting or Suspended
  • Method Name : The latest method invoked or predefined status or stack trace pattern for example, IDLE, LISTEN and KEEP-ALIVE
  • Java Stack Trace : Java Stack Trace is shown when a thread is selected.
  • Native Stack Trace : Native Stack Trace is shown below Java Stack Trace if it’s available

 

 

NOTE:

  • An IDLE thread is a thread that is ready to receive work but does not have a connection established.
  • A KEEP-ALIVE thread is an idle thread that is ready to receive work and does have a connection established.
  • A LISTEN thread listens on a port

 

 

8.If  a thread owns monitors, thread name is marked with a special icon and threads that are waiting for monitor lock are displayed.

In the following example, the thread,Plato priority9, has monitor ownership icon. In waiting threads list, Aristotle and Socrates threads are displayed.

 

 

 

If a thread is waiting for monitor lock, thread name is marked with a special icon and thread that owns monitor lock is displayed.

In the following example, the thread,Aristotle priority5, has waiting for monitor lock icon in name column.

In Blocked by list, Plato priority9 which owns the monitor is displayed:

 

 

 

 

9. You can sort any column by clicking on table header. The following is Thread Detail View sorted by thread state

 

 

 

 

10. The following is Thread Detail View sorted by method.

 

 

 

 

11.Deadlock threads are shown in gray by default as follows:

 

 

 

12. Select multiple Javacore by holding Ctrl key and click on right button of your mouse and select Compare Threads

 Or select Analysis -> Compare Threads to display Compre Threads View

 

 

 

13.  The following is Compre Threads View of two Javacore.


Summary is displayed in the right pane. Thread name and method name are displayed from each Javacore on the table.

 

  • Process ID
  • First Dump : Timestamp of the first Javacore
  • Last Dump : Timestamp of the last  Javacore
  • Garbage Collections per Minute :  Number of Garbage Collections per minute
  • Allocation Failures per Minute : Number of Allocation Failures per minute
  • Elapsed Time : Time between the first Javacore and the last Javacore
  • Number of hang suspects
  • List of hang suspects

 

 

 

 

Red background indicates there’s possible hang in the thread. Green border indicates runnable thread. Blue border indicates waiting on monitor. Yellow border indicates waiting on condition.

 

14. Thread summary is displayed when a thread is selected.

 

 

 

15. Select Monitor Detail to display Monitor Detail View

 

 

 

 

 

16.    Monitor lock information is displayed in tree format

 

Total Size is total number of threads directly and indirectly related with a specific monitor.

Size is number of threads directly related with a specific monitor.

 

 

Click on one of the threads.

 

 

There are 47 threads waiting for the monitor which is locked by Servlet Engine Transports : 3049

 

 

Deadlock threads are displayed with the icon,

as follows:

 

 

17. You can click on any thread to display detailed information about monitor and thread information

 

 

 

18. You can also compare Monitor in Compare Monitor View. Select Compare Monitors Menu

 

 

 

19. Red line indicates there’s possible hang in a specific thread in Compare Monitors View.

 

 

If you click on a thread, list of threads waiting for the locks that the thread owns is displayed in the middle pane. If you click on any thread in the middle pane, thread information is displayed on the right pane.

 

20. Select View-> Option to configure IBM Thread and Monitor Dump Analyzer for Java.


 

 

21. You can change default directory, color and other options.

 

 

22-1. Select a state in Color and click on Change button to change color.

 

 

 

 

22-2. Select a color and click on OK

 

 

 

23.In Advanced pane of option , you can customize stack trace pattern recognition engine:

 

·        ID List : ID list of customized stack trace pattern

·        ID : Identifier of stack trace pattern. No spaces allowed.

·        Stack Name : Stack trace pattern name that is seen from Thread Detail View

·        Stack Trace : Stack trace pattern. Syntax : “at package.class.method”.   No parenthesis allowed.

·        Description : description of a stack trace pattern

·        The tool will search for stack trace pattern and display Stack Name in Thread Detail View if any matching is found

·        WARNING : Please reopen thread dumps if new stack trace patterns are added if thread dumps are already loaded.

 

 

 

24. You can delete message in console by selecting Clear Console Menu

 

 

 

 

25. Select Close Thread Dumps to unload Javacore files.

 

 

26. Help menu displays help screen.

 

 

 

27. Select Help->About to display version information of IBM Thread and Monitor Dump Analyzer for Java

 

 

 

Copyrights and Notices

 

(C) Copyright IBM Corporation 2005-2008. All Rights Reserved. Note to U.S. Government Users Restricted Rights -- Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.

 

AIX, Cloudscape, DB2, IBM, iSeries, OS/390, WebSphere, and z/OS are trademarks or registered trademarks of IBM Corporation in the United States and/or other countries.

 

Java and all Java-based trademarks and logos are trademarks of Sun Microsystems, Inc. in the United States and/or other countries.

 

Microsoft, Windows, and Windows NT are trademarks of Microsoft Corporation in the United States and/or other countries.

 

UNIX is a registered trademark of The Open Group.

 

Other company, product, and service names may be trademarks or service marks of others.

 

The following paragraph does not apply to the United Kingdom or any other country where such provisions are inconsistent with local law:

 

INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS TOOLS "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you. This information could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the tool. IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this tools at any time without notices.


참고페이지 : http://starplaying.tistory.com/82

:
Posted by 라면스프
2008. 11. 13. 19:57

Memory Leak Enjoy/JAVA2008. 11. 13. 19:57

출처 : http://wiki.ex-em.com/index.php/Memory_Leak#Memory_Leak.EA.B3.BC_Out_Of_Memory_Error


Memory Leak

EXEM Knowledge Base

Jump to: navigation, 찾기

목차

[숨기기]

[편집] Memory Leak의 개요

엄밀하게 말하면 Java에서는 C/C++에서와 같은 Memory Leak이 존재하지 않는다. 적어도 JVM의 규약(Specification)상으로는 그렇다. Java에서는 한번 사용된 후 더 이상 사용되지 않는 객체, 즉 더 이상 참조(Reference)되지 않는 객체는 Garbage Collector에 의해 자동으로 정리된다. 따라서 Memory Leak이 발생할 수 없다.

Java에서의 Memory Leak은 더 정확하게 말하면 Unintentional Object Retention, 즉 [의도하지 않은 오브젝트의 계속 보유]를 의미한다. 오브젝트를 생성한 후 참조 관계를 정리하지 못한 경우, Garbage Collector는 해당 오브젝트가 사용될 가능성이 있는지 없는지를 판단할 수 없기 때문에 메모리 해제 작업을 수행할 수 없다. 이런 오브젝트들 해제되지 않고 계속 상주하면 Java Heap이 불필요한 오브젝트들로 꽉 차게 되고 Out Of Memory Error가 발생하게 된다. 즉, Java에서의 Memory Leak은 Unintentional Object Retention에 의한 Out Of Memory Error의 발생으로 요약 정리할 수 있다.

[편집] Memory Leak과 Out Of Memory Error

Memory Leak이 있는 경우에는 메모리 공간이 점진적으로 불필요한 객체들로 가득차게 되며, 필연적으로 Out Of Memory Error가 발생한다. 따라서 Out Of Memory Error가 발생하면 우선 메모리 공간의 크기를 검증하고, 다음으로 Memory Leak의 발생 가능성을 검토해 보아야 한다.

불행하게도 Memory Leak이 발생하는 장소를 정확하게 찾아내는 것은 어려운 경우가 많다. 필요한 경우 HProf와 같은 기본적인 ProfilerJProfiler, JProbe 등과 같은 Profiler를 통해서 메모리 분석을 수행해야 한다.

[편집] Memory Leak과 성능

Memory Leak이 단지 OOME만을 일으키는 것은 아니다. Memory Leak이 발생하면 Java Heap 공간의 여유 공간이 잘 확보되지 않기 때문에 계속적인 Garbage Collection이 발생한다. 불행 중 다행으로 OOME가 발생하지 않더라도 계속적인 GC 작업에 의해 성능이 저하된다. 따라서 GC Dump를 분석할 때 Memory Leak의 발생 가능성이 있는지 검토해야 한다.

[편집] Memory Leak 검증 툴

다음과 같은 툴들을 통해서 Memory Leak 현상을 분석하고 검증할 수 있다.

  1. GC Dump
  2. HProf
  3. HAT
  4. HA
  5. VisualVM
:
Posted by 라면스프
2008. 11. 13. 19:55

GC Dump 보기 Enjoy/JAVA2008. 11. 13. 19:55

출처 : http://wiki.ex-em.com/index.php/GC_Dump



GC Dump

EXEM Knowledge Base

Jump to: navigation, 찾기

목차

[숨기기]

[편집] 개요

GC Dump는 JVM의 Heap 사용 현황을 파악하는 가장 기본적인 도구이다. GC Dump를 이용하면 다음과 같은 정보를 얻을 수 있다.

  • GC 발생 시간
  • GC 수행 소요 시간
  • GC 발생 당시 Heap Usage

GC Dump는 시스템에 부하는 부하가 작기 때문에 운영 시스템에서 GC Dump를 남기는 옵션을 켜둘 것을 권장한다. 만일 메모리 문제가 발생했을 경우 GC Dump만으로 정보가 부족할 경우에는 Heap Dump를 이용해서 추가적인 분석을 수행할 수 있다. 단, Heap Dump는 시스템에 상당한 부하를 줄 수 있기 때문에 꼭 필요한 경우에만 사용해야 한다.

[편집] Sun Hotspot JVM

Sun Hotspot JVM에서는 다음과 같은 옵션들을 이용해서 GC Dump를 제어한다.

  • PrintGCDetails : GC 수행 상세 정보를 출력한다.
  • PrintGCTimeStamps : GC 발생 시간 정보를 출력한다.
  • PrintHeapAtGC : GC 발생시 Heap 상세 정보를 출력한다.
  • -Xloggc:<file>: GC Dump를 저장할 파일명을 지정한다. 따로 지정하지 않으면 Console에 바로 출력된다.


[편집] 기본 포맷

GC Dump의 출력 결과는 기본적으로 다음과 같은 포맷을 지닌다.

  • 시간(JVM 시작이후의 시간)
  • Generation이름(DefNew+Tenured, PSYoungGen+PSOldGen, ParNew+CMS-concurrent-mark)
  • Heap Usage 변동: {GC전 Usage} -> {GC후 Usage}({Total Size})
    • 예를 들어 896K->64K(960K) 이면 GC 전에는 896K를 사용했으며, GC 후 64K로 사용량이 줄었으며, 전체 크기는 960K라는 의미이다.
  • GC 소요 시간: GC를 수행하는데 걸린 시간

아래 간단한 예제가 있다.

0.186: [GC 0.186: [DefNew: 896K->64K(960K), 0.0022028 secs] 896K->434K(5056K), 0.0023143 secs]

위의 예제가 의미하는 바는 다음과 같다.

  • JVM 구동후 0.186 초에 수행된 GC 작업이다.
  • DefNew는 Default Serial Collector에서의 Young Generation을 의미한다. 즉 Minor GC가 수행되었다.
  • Young Generation의 크기는 960K이며, 896K를 사용 중이었고, GC 작업에 의해 64K만을 사용하게 되었다. 즉, GC에 의해 832K(896-64)가 Collection 되었다.
  • Minor GC 작업에 0.0022028 초가 소요되었다.
  • 전체 Heap 크기는 5056K이며, Minor GC에 의해 사용량이 896K에서 434K로 줄어들었다.
  • Minor GC를 포함해 GC를 수행하는데 총 소요된 시간은 0.0023143 초이다.

[편집] Collector 종류별 포맷

[편집] Serial Collector

Serial CollectorUseSerialGC 옵션을 이용해 활성화된다. Serial CollectorYoung GenerationOld Generation 정리 작업에 모두 Serial 방식, 즉 하나의 Thread(Single Thread)를 사용해서 GC 작업을 수행한다. Serial Collector에 의한 GC Dump에는 다음과 같은 세 가지 종류의 로그가 기록된다.

-- Minor GC
0.186: [GC 0.186: [DefNew: 896K->64K(960K), 0.0022028 secs] 896K->434K(5056K), 0.0023143 secs]
-- Major GC (Promotion Failure 발생)
1.388: [GC 1.388: [DefNew: 3839K->383K(3840K), 0.0064941 secs]
 1.394: [Tenured: 50563K->49784K(50592K), 0.1538941 secs] 53367K->49784K(54432K), 0.1605942 secs]
-- Full GC
2.425: [Full GC 2.425: [Tenured: 60543K->60543K(60544K), 0.1595010 secs] 65087K->64787K(65088K), 
 [Perm : 391K->391K(12288K)], 0.1596044 secs]

Serial Collector에서의 GC Dump의 전체 샘플은 Serial Collector GC Log Sample을 참조한다.

[편집] Parallel Collector

Parallel CollectorUseParallelGC 옵션을 이용해 활성화된다. Parallel CollectorYoung Generation 정리 작업에 Parallel 방식, 즉 여러 개의 Thread를 사용한다. Enterprise 급의 대형 Application에서는 매우 큰 크기의 Young Generation을 사용하게 된다. 이 경우 Minor GC에 많은 시간이 걸릴 수 있다. Parallel CollectorMinor GC 작업에 Multi Thread를 사용함으로써 Minor GC에 의한 Pause Time을 최소화한다. UseParallelOldGC 옵션을 이용하면 Old Generation에 대해서도 Parallel 방식을 사용할 수 있다.
Parallel Collector에 의한 GC Dump에는 다음과 같은 종류의 로그가 기록된다.

-- Minor GC
0.893: [GC [PSYoungGen: 28023K->10311K(25728K)] 49157K->38181K(56000K), 0.0556451 secs]
-- Full GC
1.460: [Full GC [PSYoungGen: 4032K->0K(26880K)] [PSOldGen: 49402K->51007K(66752K)] 53434K->51007K(93632K) 
 [PSPermGen: 2003K->2003K(12288K)], 0.1678135 secs]

Parallel Collector에서의 GC Dump의 전체 샘플은 Parallel Collector GC Log Sample을 참조한다.

[편집] CMS Collector

CMS CollectorUseConcMarkSweepGC 옵션에 의해 활성화된다. CMS CollectorFull GC에 의한 Pause Time을 최소화하기 위해 Old Generation에 대한 정리 작업을 Concurrent 방식으로 진행한다. CMS Collector에 의한 GC Dump에는 다음과 같은 종류의 로그가 기록된다.

-- Minor GC
0.138: [GC 0.138: [ParNew: 26240K->3264K(29504K), 0.0503947 secs] 26240K->8241K(521024K), 0.0505461 secs]
-- Concurrent GC
2.593: [GC [1 CMS-initial-mark: 259838K(491520K)] 263365K(521024K), 0.0023419 secs]
2.596: [CMS-concurrent-mark-start]
4.357: [CMS-concurrent-mark: 0.655/1.761 secs]
4.357: [CMS-concurrent-preclean-start]
4.419: [CMS-concurrent-preclean: 0.040/0.062 secs]
4.419: [CMS-concurrent-abortable-preclean-start]
4.428: [CMS-concurrent-abortable-preclean: 0.007/0.010 secs
4.588: [GC[YG occupancy: 3619 K (29504 K)]4.588: [Rescan (parallel) , 0.0148329 secs]4.603: [weak refs processing, 0.0000291 secs] [1 CMS-remark: 422939K(491520K)] 426559K(521024K), 0.0149645 secs]
4.688: [CMS-concurrent-sweep-start]
5.425: [CMS-concurrent-sweep: 0.302/0.737 secs]
5.425: [CMS-concurrent-reset-start]
5.489: [CMS-concurrent-reset: 0.014/0.064 secs]

CMS Collector에서의 GC Dump의 전체 샘플은 CMS Collector GC Log Sample을 참조한다.


[편집] Memory Leak과 GC Dump

Memory Leak이 발생한 경우 GC Dump은 다음과 같은 전형적인 패턴을 보인다.

1.564: [GC 1.564: [DefNew: 4543K->447K(4544K), 0.0074507 secs] 55108K->52239K(65088K), 0.0075322 secs]
1.576: [GC 1.576: [DefNew: 4543K->447K(4544K), 0.0084435 secs] 56335K->54675K(65088K), 0.0085257 secs]
1.589: [GC 1.589: [DefNew: 4543K->447K(4544K), 0.0072420 secs] 58771K->55902K(65088K), 0.0073378 secs]
1.600: [GC 1.600: [DefNew: 4543K->447K(4544K), 0.0073699 secs] 59998K->57130K(65088K), 0.0074590 secs]
1.610: [GC 1.610: [DefNew: 4543K->447K(4544K), 0.0075529 secs] 61226K->58357K(65088K), 0.0076395 secs]
1.621: [GC 1.621: [DefNew: 4543K->447K(4544K), 0.0074387 secs] 62453K->59585K(65088K), 0.0075247 secs]
1.632: [GC 1.632: [DefNew: 4543K->4543K(4544K), 0.0000433 secs] 63681K->63681K(65088K), 0.0001028 secs]
1.632: [Full GC 1.632: [Tenured: 59137K->57835K(60544K), 0.2154176 secs] 63681K->57835K(65088K), [Perm : 392K->391K(12288K)], 0.2155249 secs]
1.851: [GC 1.851: [DefNew: 4096K->447K(4544K), 0.0057781 secs] 61931K->59063K(65088K), 0.0058661 secs]
1.860: [GC 1.860: [DefNew: 4543K->447K(4544K), 0.0071495 secs] 63159K->60291K(65088K), 0.0072347 secs]
1.870: [GC 1.871: [DefNew: 4543K->4543K(4544K), 0.0000335 secs]1.871: [Tenured: 59843K->60543K(60544K), 0.1666050 secs] 64387K->61519K(65088K), 0.1667678 secs]
2.038: [Full GC 2.038: [Tenured: 60543K->60543K(60544K), 0.1665533 secs] 62712K->61855K(65088K), [Perm : 391K->391K(12288K)], 0.1666667 secs]
2.234: [Full GC 2.234: [Tenured: 60543K->60543K(60544K), 0.1607975 secs] 65087K->64658K(65088K), [Perm : 391K->391K(12288K)], 0.1609087 secs]
2.425: [Full GC 2.425: [Tenured: 60543K->60543K(60544K), 0.1595010 secs] 65087K->64787K(65088K), [Perm : 391K->391K(12288K)], 0.1596044 secs]

위의 패턴은 다음과 같이 해석할 수 있다.

[편집] IBM JVM

IBM JVM에서는 다음 옵션들을 이용해서 GC Dump를 제어한다.

  • verbosegc : GC Dump를 남길 것을 지정한다. (예: -verbosegc)
  • verbosegclog : GC Dump를 남길 File 이름을 지정한다. (예: -Xverbosegclog:gc.log)

IBM JVM은 Sun Hotspot JVM과는 달리 XML 포맷의 로그를 남긴다. 일반적인 포맷(Generational Collection을 사용하지 않는 경우)은 다음과 같다.

<af type="tenured" id="1" timestamp="Tue Oct 23 00:33:18 2007" intervalms="0.000">
  <minimum requested_bytes="32" />
  <time exclusiveaccessms="0.037" />
  <tenured freebytes="209408" totalbytes="4194304" percent="4" >
    <soa freebytes="0" totalbytes="3984896" percent="0" />
    <loa freebytes="209408" totalbytes="209408" percent="100" />
  </tenured>
  <gc type="global" id="1" totalid="1" intervalms="0.000">
    <expansion type="tenured" amount="1048576" newsize="5242880" timetaken="0.010" reason="insufficient free space following gc" />
    <refs_cleared soft="6" weak="8" phantom="0" />
    <finalization objectsqueued="9" />
    <timesms mark="5.154" sweep="0.138" compact="0.000" total="5.405" />
    <tenured freebytes="1696464" totalbytes="5242880" percent="32" >
      <soa freebytes="1520336" totalbytes="5066752" percent="30" />
      <loa freebytes="176128" totalbytes="176128" percent="100" />
    </tenured>
  </gc>
  <tenured freebytes="1695728" totalbytes="5242880" percent="32" >
    <soa freebytes="1519600" totalbytes="5066752" percent="29" />
    <loa freebytes="176128" totalbytes="176128" percent="100" />
  </tenured>
  <time totalms="5.590" />
</af>

용어의 의미는 다음과 같다.

  • af는 Allocation Failure의 약자로 새로운 Object를 위한 메모리를 할당받는데 실패했다는 것을 의미한다.
  • gc는 Garbage Collection의 약자로 Allocation Failure에 의해 GC가 발생했다는 것을 의미한다.
  • soa는 Small Object Area의 약자로 크기가 작은 Object들이 거주하는 공간을 의미한다.
  • loa는 Large Object Area의 약자로 크기가 큰 Object들이 거주하는 공간을 의미한다.
  • 'expansion은 Free Memory 부족으로 인해 Heap Expansion이 발생했다는 것을 의미한다.
  • intervalms, timetaken, timems, totalms 등의 값을 통해 GC 수행 시간 정보를 알 수 있다.

이 용어를 이용해서 위의 GC Log를 분석하면 다음과 같은 사실들을 알 수 있다.

  • GC 작업 이전의 Total Heap은 4M(4194304)이며, Free Heap은 200K(209408)였다.
  • GC 과정에서 Expansion이 발생했으며 1M(1048576)만큼 Heap 크기가 증가했다.
  • Mark에 5.154ms, Sweep에 0.138ms가 소요되었으며 Compaction은 일어나지 않았다.
  • GC 작업 후 Free Heap은 1.6M(1695728), Total Heap은 5M(5242880)로 증가했다.


[편집] Throughput Collector

Throughput Collectorgcpolicy(-Xgcpolicy:optthruput) 옵션에 의해 활성화된다. Throughput, 즉 일량을 최대화하게끔 작동한다. Throughput Collector에 의한 GC Dump에는 다음과 같은 정보가 기록된다.

...            
<af type="tenured" id="21" timestamp="Tue Oct 23 00:33:27 2007" intervalms="1441.639">
  <minimum requested_bytes="32" />
  <time exclusiveaccessms="0.055" />
  <tenured freebytes="0" totalbytes="764988928" percent="0" >
    <soa freebytes="0" totalbytes="764988928" percent="0" />
    <loa freebytes="0" totalbytes="0" percent="0" />
  </tenured>   
  <gc type="global" id="21" totalid="21" intervalms="1441.756">
    <compaction movecount="1913517" movebytes="123114336" reason="compact to aid heap contraction" />
    <contraction type="tenured" amount="38248960" newsize="726739968" timetaken="0.949" reason="excess free space following gc" />
    <refs_cleared soft="0" weak="0" phantom="0" />
    <finalization objectsqueued="0" />
    <timesms mark="315.329" sweep="5.649" compact="443.578" total="767.525" />
    <tenured freebytes="603616120" totalbytes="726739968" percent="83" >
      <soa freebytes="603616120" totalbytes="726739968" percent="83" />
      <loa freebytes="0" totalbytes="0" percent="0" />
    </tenured> 
  </gc>        
  <tenured freebytes="603550584" totalbytes="726739968" percent="83" >
    <soa freebytes="603550584" totalbytes="726739968" percent="83" />
    <loa freebytes="0" totalbytes="0" percent="0" />
  </tenured>   
  <time totalms="767.678" />
</af>   
...

IBM JVM은 기본적으로 Generation을 사용하지 않기 때문에 Minor GCMajor GC의 구분이 없다. Generation을 사용하지 않기 때문에 항상 모든 Heap 영역이 GC 대상이 된다. gc type="global"이 의미하는 바가 이것이다.

[편집] Response Time Collector

Response Time Collectorgcpolicy(-Xgcpolicy:optavgpause) 옵션에 의해 활성화된다. Response Time, 즉 응답 시간을 최적화하게끔 작동한다. Response Time Collector에 의한 GC Dump에는 다음과 같은 정보가 기록된다.

...
<af type="tenured" id="9" timestamp="Tue Oct 23 00:34:10 2007" intervalms="8099.846">
  <minimum requested_bytes="88" />
  <time exclusiveaccessms="0.036" />
  <tenured freebytes="0" totalbytes="1072750592" percent="0" >
    <soa freebytes="0" totalbytes="1072750592" percent="0" />
    <loa freebytes="0" totalbytes="0" percent="0" />
  </tenured>
  <con event="completed full sweep" timestamp="Tue Oct 23 00:34:10 2007">
    <stats sweepbytes="0" sweeptime="0.088" connectbytes="0" connecttime="0.003" />
  </con>
  <gc type="global" id="20" totalid="20" intervalms="1752.723">
    <refs_cleared soft="0" weak="0" phantom="0" />
    <finalization objectsqueued="0" />
    <timesms mark="320.279" sweep="218.930" compact="0.000" total="539.259" />
    <tenured freebytes="261405180" totalbytes="1072750592" percent="24" >
      <soa freebytes="261405180" totalbytes="1072750592" percent="24" />
      <loa freebytes="0" totalbytes="0" percent="0" />
    </tenured>
  </gc>
  <tenured freebytes="261404428" totalbytes="1072750592" percent="24" >
    <soa freebytes="261404428" totalbytes="1072750592" percent="24" />
    <loa freebytes="0" totalbytes="0" percent="0" />
  </tenured>
  <time totalms="539.514" />
</af>
...

Response Time Collector 또한 Generation을 사용하지 않는다. 따라서 Throughput Collector와 같이 gc type="global"로 표현된다.

[편집] Concurrent Generational Collector

Concurrent Generational Collectorgcpolicy(-Xgcpolicy:gencon) 옵션에 의해 활성화된다. IBM JDK 1.5에서 새롭게 추가되었으며 Sun Hotspot JVM의 CMS Collector와 매우 유사한 기법을 사용한다. Concurrent Generational Collector에 의한 GC Dump에는 다음과 같은 로그가 기록된다.

...
<af type="nursery" id="91" timestamp="Tue Oct 23 00:34:50 2007" intervalms="27.674">
  <minimum requested_bytes="32" />
  <time exclusiveaccessms="0.053" />
  <nursery freebytes="0" totalbytes="38255104" percent="0" />
  <tenured freebytes="92785912" totalbytes="372438528" percent="24" >
    <soa freebytes="92785912" totalbytes="372438528" percent="24" />
    <loa freebytes="0" totalbytes="0" percent="0" />
  </tenured>
  <gc type="scavenger" id="91" totalid="101" intervalms="27.755">
    <flipped objectcount="161001" bytes="9015948" />
    <tenured objectcount="300702" bytes="16428048" />
    <refs_cleared soft="0" weak="0" phantom="0" />
    <finalization objectsqueued="0" />
    <scavenger tiltratio="60" />
    <nursery freebytes="30438400" totalbytes="40268800" percent="75" tenureage="1" />
    <tenured freebytes="74960120" totalbytes="372438528" percent="20" >
      <soa freebytes="74960120" totalbytes="372438528" percent="20" />
      <loa freebytes="0" totalbytes="0" percent="0" />
    </tenured>
    <time totalms="86.476" />
  </gc>
  <nursery freebytes="30436352" totalbytes="40268800" percent="75" />
  <tenured freebytes="74960120" totalbytes="372438528" percent="20" >
    <soa freebytes="74960120" totalbytes="372438528" percent="20" />
    <loa freebytes="0" totalbytes="0" percent="0" />
  </tenured>
  <time totalms="86.610" />
</af>
...

Concurrent Generational Collector는 Generation에 기반한다. 따라서 Minor GCMajor GC와 같은 구분이 존재한다. IBM JVM에서는 Scavenger GCGlobal GC라는 용어가 사용된다. 위의 예에서 af type="nursery", gc type="scavenger"가 의미하는 것은 Nursery, 즉 Young Generation에서 GC가 발생했으며, Scavenger GC, 즉 Minor GC가 발생했다는 것을 의미한다.

[편집] 관련 정보

  1. Garbage Collector

[편집] 외부 참조

 
:
Posted by 라면스프

출처 : http://kwon37xi.egloos.com/3666564

OKJSP에 자주 가서 요즘 자바 개발자들이 어떻게 살아가나를 보는 편인데, 아주 많이 반복적으로 올라오는 질문이 "대체 뭘 공부해야 하나요? 프레임워크는 Spring을 해야 할까요? iBATIS를 해야 할까요?" 하는 식의 질문들이다(이 질문은 사실 말이 안된다. 왜 그런지 읽다보면 나온다).

Java는 웹 관련 프레임워크들이 너무 다양하고, Ruby나 Python 같은 경우에는 RubyOnRailsDjango 처럼 하나의 프레임워크 안에 기능별 프레임워크들도 모두 다 All in one 형태로 들어 있어서 혼란을 주지 않는 반면, Java는 각 영역별로 프레임워크가 모두 다르고, 또한 각 영역별로 존재하는 프레임워크들의 종류도 많아서 초보 개발자들에게 극심한 혼란을 주고 있다.

그래서 나름대로 Java Web 개발자들을 위한 학습 로드맵을 정리해 보았다.

1. Java 그 자체
많은 웹 개발자들이 마치 JSP 코드를 짤 줄 알면 그걸로 Java 웹 개발을 할 줄아는 것이라 생각하고 Java 그 자체를 소홀히 하는 것을 본다.
말도 안되는 소리이다. Java를 모르고서 Java 웹 개발을 제대로 한다는 것은 어불 성설이다. Java 그 자체를 먼저 공부하라.

특히 Java 5 문법을 숙지하길 권한다. 이제 우리나라도 점차 Java 5가 대세가 되어 가고 있다. 대부분의 프레임워크들과 WAS(JSP와 서블릿을 구동하는 서버)도 모두 Java 5를 기준으로 바뀌었으며, JVM 자체도 버전이 높을 수록 성능이 더 좋다.

2. JSP와 Servlet 그리고 Model 1
모델 1은, JSP 하나에 DB에 접속해서 쿼리를 날리는 등의 모든 업무적인 기능(Business Logic)을 넣고, 그 아래에 HTML 코드를 박아 넣는 식으로 개발하는 것을 의미한다.
아직도 많은 개발자들이 여기에 길들여져 있는데, 일단 JSP 자체에 대한 기본기를 익힌 뒤로는 재빨리 버려야 할 습관이다.

그리고 많은 개발자들이 Servlet을 무시하고 JSP만 하는 것을 보곤 하는데, Servlet에 대한 학습이 제대로 이뤄지지 않으면 더 나은 웹 개발이 곤란하다. Servlet에 대한 기초 개념을 확실히 잡길 권한다.

3. Model 2 - 프레임워크의 등장
JSP로 열심히 개발을 하다보니 프로젝트 규모도 커지기 시작하고, JSP 파일 크기도 수천줄에 달하는등 엄청나게 커진다.
그런데 이 JSP에다 두서없이 모든 기능을 다 때려 넣다보니 JSP마다 똑같은 기능들이 Copy&Paste로 들어가고, JSP 안에 들어 있는 Java 코드들에서 에러가 발생하면 찾아내서 디버깅 하는 일이 지옥같이 느껴지기 시작한다.

여기서 Model 2가 구원자로 등장한다.

Model 2는 말만 멋드러졌지 실제로는 간단한 개념이다.

JSP에서 수행하던 DB 쿼리 등의 작업을 Servlet에게 넘겨주고 JSP에서는 오로지 화면 출력만 담당하는 것이다.

Servlet에서 DB 쿼리등 화면 출력과는 상관없는 비지니스 로직을 일단 먼저 모두 수행하고, 그 결과를 request.setAttribute("key",결과객체);로 담은 다음 JSP 페이지로 포워딩(forward)을 하면 JSP에서는 request.getAttribute("key")로 그 객체를 받아서 화면에 뿌려주기만 한다.
이런 업무 수행단/화면 출력단의 철저한 역할 분리가 Model 2이다.

여기서 이러한 각 역할을 "MVC - Model View Controller" 라고 한다. 그래서 Model 2는 MVC와 동일한 의미로 사용하기 도 한다. MVC의 의미는 공부하면서 찾아보라.

이게 뭐가 좋냐고? 개발 기간이 좀 길어지고 프로젝트 규모가 쬐끔 커지고, 기존 프로젝트를 유지보수를 해보면 얼마나 좋은지 몸소 뼈져리게 느끼게 된다.

Model 2의 기능을 정형화해서 쉽게 구현하게 해주는 것이 MVC Framework들의 역할이다.
가장 유명한 Model 2 웹 프레임워크들은 다음과 같은 것들이 있다.

* 스트럿츠 1 - Struts 1
* 스트럿츠 2 - Struts 2
* 스프링 MVC - Spring MVC
* 기타 덜 유명한 Wicket, Stripes, JSF, Tapestry 등.

Struts 1은 MVC의 효시라고 할 수 있다. 우리에게 MVC라는 축복을 주기는하였으나, 나온지 오래된 만큼 낡은 개념들이 많이 녹아있고 쓸데 없이 복잡하고 배우기도 어려운 편이다.

오히려 Struts 2와 Spring MVC가 더 배우기 쉬울 것이며, 개발도 더 쉽다. 현재 추세는 Struts 2와 Spring MVC이다. 대형 포탈이나 SI 업체들도 Spring/Struts 2를 주로 채택하는 추세로 가고 있는 것으로 알고 있다.

둘 중 하나의 개념만 확실히 이해해도 다른 것을 배우는데 어려움이 별로 없으므로 그냥 둘중에 골라서 배우길 권한다. 나는 Spring을 선호한다.

그리고 MVC 프레임워크를 사용하기 시작하면서 View를 만드는 JSP에 대해서도 재조명이 시작된다. 기존에 Java 코드를 JSP에 직접 넣던 관행을 버리고 JSTL과 태그 라이브러리를 사용하거나 아예 JSP를 버리고 다른 템플릿 엔진으로 만들기도 한다. 이에 관해서는 맨 마지막에.

4. 퍼시스턴스 프레임워크 : JDBC 반복 작업에 짜증이 나기 시작하다.
현대 웹 개발에서 가장 큰 역할을 차지하는 것은 뭐니뭐니해도 단연 Database 작업이다.
지금까지는 아마도 JDBC에서 DB 커넥션을 맺고, 쿼리를 날리고 그 결과 ResultSet을 JSP로 넘겨주어서 출력하는 식으로 했을 것이다.
이미 다들 알고 있겠지만 JDBC를 사용하면 똑같은 코드가 굉장히 많이 반복해서 나온다. 한마디로 "삽질"의 전형이 JDBC 작업이다.
이것을 깨달은 많은 개발자들이 조금 어정짱하게 반복작업을 해결해주는 Util 클래스들을 프로젝트별로 만들어서 사용하곤 한다.
하지만, 물론 이에 대해 정형화하고 깔끔하고 훨씬 더 사용하기 쉬게 만들려는 노력이 이미 수년에 걸쳐 이루어졌다.

이렇게 DB관련된 작업을 정형화한 것들을 Persistence Framework 라고 한다.

* 아이바티스 - iBATIS : SQL Mapper - JDBC보다 더 쉽게 배우고, 더 편하게 사용한다.
* 하이버네이트 - Hibernate : 객체지향을 객체지향답게, 개발 기간을 엄청나게 단축시켜주다.

퍼시스턴스 프레임워크의 양대 산맥은 iBATIS와 Hibernate이다. 이 둘 모두 우리나라에 책이 나와 있다.
iBATIS는 SQL Mapper의 한 종류이고, Hibernate는 ORM의 한 종류이다.

이 둘의 차이는 iBATIS는 개발자가 SQL 쿼리를 직접 작성한 것을 객체에 매핑시켜주는 것이고, ORM은 DB 스키마와 객체간의 관계를 설정파일로 만들면 자동으로 쿼리를 만들어주는 것이다.

자, 이 둘을 보면 미국에서는 Hibernate가 인기가 좋고, 우리나라에서는 iBATIS가 사실상 SI 업계를 평정했다.
그러니까, 일단은 우리나라에서는 iBATIS를 공부하면 된다고 보면 된다.

이렇게 말하니까 마치 이 둘이 경쟁자 같은데, 사실 이 둘은 경쟁 상대라기 보다는 보완해주는 역할을 한다. SI에서 처럼 DB 테이블이 정규화 되어 있지 않은 경우에는 Hibernate같은 ORM을 사용하면 프로젝트를 말아먹을 수 있다.

iBATIS는 테이블 정규화에 무관하게, 개발자가 작성한 SQL을 객체로 매핑하기 때문에 DB 스키마가 마구 꼬여 있는 상황에서도 유연하게 작동하고, 개발자가 직접 SQL 튜닝을 할 수 있다는 장점이다.

그리고 Hibernate는 배우기가 굉장히 어려운 프레임워크이고 튜닝이 매우 어렵다. Hibernate책을 보면 캐싱을 통해 성능을 향상시키라고 하지만 캐싱은 iBATIS도 못지않게 잘 지원한다. 하지만 일단 배우면, 그로인한 코딩 생산성이 iBATIS가 감히 넘볼 수 없을 정도록 급격히 향상된다.

Hibernate는 DB 정규화가 잘되어 있는 웹 포탈 업체나 패키지 소프트웨어 제작시에 강력히 권장할만 하다.

5. IoC와 DI - 객체의 생성주기와 의존성을 관리하고 싶어지다
사실 내가 경험한 SI를 보면 4단계 까지만 가도 막장은 아닌 프로젝트라고 본다. 아직도 신규 프로젝트를 하면서도 Model 1에 JDBC로 코딩하는 것을 많이 보았기 때문이다.

앞서, MVC라는 형태로 웹 애플리케이션의 역할을 철저하게 분할해서 처리하라고 했었다.

이제 여기서 좀 더 역할을 분할하기 시작한다.

Database를 관장하는 코드(DAO)와 Database 처리 결과를 가지고 그외 비지니스 로직을 추가로 수행하는 코드(Service), 그리고 웹으로 들어온 요청을 받아서 비지니스 로직을 호출하고, 그 결과를 다시 웹(HTML 등)으로 내보내는 코드(Controller)로 분할을 하면 유지보수가 더 쉽고, DB가 Oracle에서 DB2 로 변경되는 식의 중대 변화가 있을 때도 DAO만 바꾸면 되는 식으로 변화에 대한 대처가 유연해 진다는 것을 깨닫기 시작한다.

이제는 각 역할별로 클래스를 분할하고 컨트롤러 객체는 서비스 객체에 서비스 객체는 DAO 객체에 의존해서 작동하도록 코드를 바꾸기 시작한다. 그리고 객체의 생성과 파괴 주기도 관리해야만 하게 된다. 객체를 하나만 생성하면 되는데 불필요하게 매번 new를 할 필요는 없으니까.

이렇게 객체의 생성/파괴 주기를 관리하고 객체간의 의존성을 관리해주는 프레임워크를 IoC 컨테이너라고 부른다.

1. Spring Framework
2. EJB 3.0

사실상 대세는 Spring Framework로 굳어졌다. EJB 3.0은 내가 안써봐서 뭐라 말은 못하겠다.

Spring MVC는 이 Spring Framework의 일부분이다.

Spring은 또한 AOP도 지원한다.

AOP 의 개념이 상당히 어려운 편이라서 개념 자체를 확실히 한마디로는 표현하지 못하겠다. 어쨌든 개발자들에게 가장 쉽게 다가오는 표현으로 하자면, AOP는 동일한 패턴으로 반복적으로 해야하는 일을 설정을 통해 자동으로 해주는 것이다.
이에 관한 가장 보편적인 예가 바로 트랜잭션이다.
지금까지는 아마도 비지니스 로직이 시작될 때 트랜잭션이 시작되고, 비지니스 로직이 끝날 때 트랜잭션을 종료하는 코드를 매번 작성해서 넣었을 것이다.
AOP를 사용하면, 비지니스 로직의 역할을 하는 메소드가 무엇인지 설정파일에 넣어주기만 하면 자동으로 메소드가 시작될 때 트랜잭션을 시작시키고, 메소드가 끝날 때 트랜잭션을 종료시켜준다. 물론 예외가 발생하면 트랜잭션을 rollback도 해준다. 따라서 Spring을 사용한 프로젝트에서는 트랜잭션 관련 코드를 볼 수 없을 것이다.

Spring 프레임워크는 기본적으로 IoC 컨테이너 역할을 하는 것이 핵심이다. 따라서 Spring을 사용한다고 해서 꼭 Spring MVC를 사용할 필요는 없다. Struts 2 + Spring + iBATIS 나 SpringMVC + Spring + Hibernate 등... 어떠한 조합이라도 가능하다.

6. 그 외
◈ Template Engine : JSP 보다 더 간결하면서 강력한게 필요해!
   * JSP + JSTL : Sun이 지정한 산업표준이다. JSTL은 당연히 쓰고 있으리라 믿는다.
   * Freemarker : 가장 권장할 만하다.
   * Velocity : 굉장히 배우기 쉽다. JSTL보다 더 빨리 배워서 쓸 수 있다. 가독성도 좋다. 그러나 Freemarker 만큼 편하고 강력하지는 못하다.
많은 사람들이 Java 웹 개발을 그냥 "JSP 개발"이라고도 부르는데, MVC가 도입되고, Freemarker 같은 다른 템플릿 엔진을 사용하게 되면 더이상 JSP는 코빼기도 안보이게 된다. 그러므로.. JSP 개발이라는 말은 쓰지 않았으면 좋겠다.

◈ Layout Engine
   * Sitemesh : 헤더 푸터 처럼 동일 패턴이 반복되는 레이아웃을 관리해준다.

◈ XML 도우미 : W3C DOM은 너무 어렵고 난잡하다. 좀 더 편한 XML관련 개발을 원한다면..
   * JDOM : Java 표준으로 지정됐다고 한다.
   * DOM4J
둘 다 비슷하게 편한거 같다. 예전엔 JDOM을 썼었는데, 나 같은 경우 현재 프로젝트에서는 DOM4J를 사용한다. Hibernate가 DOM4J를 사용하기 때문에, 별도의 라이브러리 더 넣는게 귀찮아서.

◈ 단위 테스트
   * jUnit : 코드를 철저하게 테스트하자.

◈ 소스코드 버전관리
   * CVS
   * Subversion : 현재 대세는 Subversion
내가 최고 막장으로 꼽는 프로젝트는 아직도 FTP로 소스 관리하는 프로젝트이다. 이런 프로젝트에는 절대로 참여하지 않을 것이라고 굳게 맹세하고 또 맹세했다. --;
소스 코드 버전관리는 여러 개발자들이 동시에 개발할 때 소스코드를 저장하고 충돌을 관리해주며, 소스 변경 내역을 계속해서 추적해서 과거 소스로 언제든지 돌아갈 수 있도록 도와준다.
현재 대세는 Subversion이지만 CVS로도 버전관리의 이점을 충분히 만끽할 수 있다. 그리고.. 사실 CVS가 사용법을 익히기는 더 쉽다.

◈ 자동 빌드
   * Ant : Ant 면 만사 Ok!
   * Maven
아직도 javac 로 컴파일하고 있고, FTP로 파일 올려서 복사하고 있다면.. 이 모든일을 자동으로 명령 한방에 처리하도록 해야 실수도 적고, 퇴근도 일찍한다.
Ant로 빌드와 배포를 자동화 하자.

결론

내가 권하는 조합은
* SI 업체에서 일하는 경우 : Struts 2 혹은 SpringMVC + iBATIS + JSP/JSTL + 가능하다면 Spring Framework
* 웹 포털등과 같은 업계, 패키지 소프트웨어 제작 업체 : Struts 2 혹은 Spring MVC + Hibernate + Spring Framework + Freemarker + Sitemesh
:
Posted by 라면스프

출처 : http://rothmans.wordpress.com/category/java


기본적인 코딩 표준

1.클래스명
  : 첫문자는 대문자로 시작하고, 구분되는 부분도 대문자로 시작한다.

2.메소드명
:첫문자는 소문자로 시작하고, 구분은 대문자로 짓는다.메소드명에는 대조할 수 있는
  이름을 붙인다.

3.섀도우 필드는 작성하지 않는다.
  :섀도우 필드, 즉 부모 클래스의 필드명과 동일한 필드명은 버그의 원인이 되므로,
   가능한 한 사용하지 않는다. 언어의 사양에 따라 컴파일러는 통과할 수 있다.

4.외부 리소스는 반드시 해제한다.
  :파일 입출력(I/O)과 데이터베이스의 커넥션,커서는 확실하게 close메소드를 실행하지
   않으면 자원이 해제되지 않는다. 반드시 finally에서 close 메소드를 실행하도록 한다.

ex) Connection = con =null;
     try
     {
     //처리
     }
     finally
     {
      if(con !=null)
      {
        con.close();
      }
     }

5.catch한 예외는 반드시 처리한다.
  :catch한 예외에 대해 아무런 대처도 하지 않으면 디버그를 곤란하게 만든다. 반드시 로그
   출력을 하거나 throw한다. 이유가 있어서 무시하게 될 경우는 무시하는 이유을 주석으로
   붙인다.







Java 성능개선을 위한 Programming 기법

JDK1.3버전 이후로 지원되는 HotSpot VM은 기본적으로 Hip에 동적으로 할당된 Object는 거의 회수할 수 있다고 한다. 하지만 이 기능으로 인해서 VM은 엄청난 OverHead를 가지게 된다. 무리한 Object의 생성은 생성 당시에도 많은 OverHead를 초래하지만, 생성된 Object를 회수하기 위해서는 더 많은 작업이 요구된다. 이를 해결하기 위한 몇 가지 Tip이 있는데, Avoiding Garbage Collection, Object 재사용, static instance variable의 사용에 의한 단일 클래스 인스턴스 구현 방법 등이 그것이다. 핵심은 가능한 Object 생성을 피하자는 것이다.

▶ 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 집약적인 작업이고, Hip 영역에 Object가 생성되고 수집되기 때문에 가비지 컬렉터 작업을 수반한다. 가능한 Object의 Instance가 필요 없는 static 메소드를 사용한다. 예제2) 참조.

▶ Avoiding Garbage Collection(임시 Object 생성 금지)
가장 흔한 예로 String Object의 append를 위해서 (+) 연산을 사용하는 것을 들 수 있다. (+) 연산자를 사용해서 String Object를 append할 경우 우리가 생각하는 것 보다 훨씬 더 많은 임시 Object가 생성되고, 가비지 컬렉터에 의해 다시 수집된다. String Object의 append연산을 위해서는 StringBuffer 클래스를 사용한다. 예제1)과 예제2) 참조

예제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를 충분히 참조한 다음 지원 메소드가 없을 경우, 클래스를 상속받아 요구에 적합한 메소드를 만드는 방법도 고려할 필요가 있다. 기존 API를 이용한 Object 초기화 방법은 아래 Object 재사용(메소드를 사용한 Object 초기화) 부분 참조.

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

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

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

▶ Object 재사용(Pool Management)
Object 재사용 기법으로 흔히 Pool Management 기법을 사용한다. 이는 임의 갯수의 Object를 미리 생성해 두고 이를 Vector 클래스를 사용해서 관리하는 방법이다. 해당 Object를 사용하기 위해서 Pool의 Vector에 있는 Object를 하나 가져오고, 다 사용하고 나면 다시 Pool에 반납한다. 이는 기존에 공개되어 있는 Hans Bergsten의 Connection-Pool의 PoolManager클래스에서 사용되고 있다.(Professional JAVA Server Programming. 정보문화사. 2000.4.4. 제9장 연결풀링 부분 참조) Object Pool을 사용할 경우, 반환되는 Object가 초기화되어 반환되지 않을 경우 다음에 Pool에서 Object를 가져와서 사용하게 되면 문제를 야기할 수 있기 때문에 초기 클래스 Design시 꼼꼼하게 따져 봐야 한다.

▶ 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를 재사용 할 수 있다.

▶ static instance variable의 사용에 의한 단일 클래스 인스턴스 구현
다음은 Hans Bergsten의 PoolManager 클래스 코드 중 일부다.

public class PoolManager{
static private PoolManager instance;
:
private PoolManager(){
init();
}
static synchronized public PoolManager getInstance(){
if (instance == null){
instance = new PoolManager();
}
:
return instance;
}
private void init(){
:
}

PoolManager형의 인스턴스가 static으로 선언되어 있다. getInstance() 메소드는 현재 생성되어 있는 PoolManager의 Object를 조사하고 만약 Object가 있으면 Object를 반환하고 없으면 생성자를 호출해서 PoolManager의 Object를 생성한 후 반환한다. 결국 JVM 내부에는 하나의 PoolManager Object가 존재하게 된다. 단일 클래스 인스턴스 기법을 사용할 경우 하나의 인스턴스를 사용하기 때문에 해당 인스턴스의 무결성 부분이 문제가 된다. 이를 위해 다양한 Synchronization 기법을 사용하게 된다. 아래 I/O 퍼포먼스 개선 부분 참조.

▶ clone() 메소드 사용으로 Object 생성에 따른 OverHead를 피함

private static int[] data = new int[2][2];
int[] someMethod(){
int[] a = (int[])this.data.clone();
return a;
}

대부분의 클래스들에는 clone() 메소드가 존재한다. clone() 메소드가 호출되면 Object의 복사본을 반환하는데, 대신 클래스의 생성자를 호출하지 않기 때문에 생성자 호출에 의한 OverHead를 피할 수 있다. clone() 메소드를 사용할 때의 trade-off 문제는 다음에 예제를 참조 할 것.

static int[] Ref_array1={1,2,3,4,5,6,7,8,9};
static int[][] Ref_array2={{1,2},{3,4},{5,6},{7,8}};

int[] array1={1,2,3,4,5,6,7,8,9}; //faster than cloning
int[] array1=(int[])Ref_array1.clone(); //slower than initializing

int[][] array2={{1,2},{3,4},{5,6},{7,8}}; //slower than cloning
int[][] array2=(int[][])Ref_array2.clone(); //faster than initializing

▶ Method Inline에 의한 method호출 감소
예제1)
public class InlineMe{
int counter=0;
public void method1(){
for(int i=0;i<1000;i++){
addCount();
System.out.println(”counter=”+counter);
}
public int addCount(){
counter=counter+1;
return counter;
}
public static void main(String args[]){
InlineMe im=new InlineMe();
im.method1();
}
}

예제1)에서 addCount() 메소드를 다음과 같이 수정하면

public void addCount(){
counter=counter+1;
}

위와 같이 수정할 경우 addCount() 메소드는 컴파일시 Inline 되어서 실제 메소드를 호출하지 않고 같은 결과를 반환한다. 즉 method1()이 실제 수행될 때는 다음과 같이 수행.

public void method1(){
for(int i=0;i<1000;i++){
counter=counter+1;
System.out.println(”counter=”+counter);
}

▶ 생성자 설계
예제1,2,3) 모두 같은 역할을 하는 생성자들로 구성되어 있다. 하지만 퍼포먼스 측면에서 보면 예제3)이 가장 효율적이다. 하지만 클래스를 설계 할 때 예제1)과 같이 해야 할 때도 있다. 클래스가 요구하는 조건에 따라 생성자의 설계방법이 다르겠지만, Object를 생성할 때 가능한 생성자를 적게 호출하는 방법을 사용하는 것이 퍼포먼스 면에서 좋다는 것은 당연한 일이다.

예제1)
예제2)
예제3)
class SlowFlow{
private int someX, someY;
SlowFlow(){
this(777);
}
SlowFlow(int x){
this(x,778);
}
SlowFlow(int x, int y)
someX=x;
someY=y;
}
}
class SlowFlow{
private int someX, someY;
SlowFlow(){
this(777,778);
}
SlowFlow(int x){
this(x,778);
}
SlowFlow(int x, int y)
someX=x;
someY=y;
}
}
class SlowFlow{
private int someX, someY;
SlowFlow(){
someX=777;
someY=778;
}
SlowFlow(int x){
someX=x;
someY=778;
}
SlowFlow(int x, int y)
someX=x;
someY=y;
}
}

▶ “extends” VS “implements”
예제1) extends
import java.awt.event.*;
import java.awt.*;
public class MyWindowAdapter extends WindowAdapter{
public void windowClosing(WindowEvent we){
Container source=(Container)we.getSource();
source.setVisible(false);
}
}

예제2) implements
import java.awt.event.*;
import java.awt.*;
public class MyWindowListener implements WindowListener{
public void windowClosing(WindowEvent we){
Container sourve=(Container)we.getSource();
source.setVisible(false);
}
public void windowClosed(WindowEvent we){}
pubic void windowActivated(WindowEvent we){}
public void windowDeactivated(WindowEvent we){}
public void windowIconified(WindowEvent we){}
public void windowDeiconified(WindowEvent we){}
public void windowOpened(WindowEvent we){}

“implements”의 경우에는 특정 메소드를 구현하고 인터페이스에 정의된 모든 메소드를 코딩해야 하기 때문에 코드의 낭비를 초래하는 반면, “extends”의 경우에는 슈퍼 클래스에 정의된 메소드들 중 필요한 메소드만 overriding 하면 된다. ==> 설계 시 추상클래스를 사용할 것인지 인터페이스를 사용할 것인지 고려.

▶ 클래스 집약
예제1)
public class Person{
private Name name;
private Address address;
}
class Name{
private String firstName;
private String lastName;
private String[] otherNames;
}
class Address{
private int houseNumber;
private String houseName;
private String streetName;
private String town;
private String area;
private String greaterArea;
private String country;
private String postCode;
}

예제1)에서 정의된 Person 클래스는 Name형의 name과 Address형의 address, 두 개의 인스턴스 변수를 가진다. 클래스를 설계할 때 가능한 클래스의 수를 줄여서 수행 시 동적으로 생성되는 Object의 수를 줄일 수도 있다. 예제1)에서 정의된 세 개의 클래스는 하나의 클래스로 집약될 수 있다.

예제2)
public class Person{
private String firstName;
private String lastName;
private String[] otherNames;
private int houseNumber;
private String houseName;
private String streetName;
private String town;
private String area;
private String greaterArea;
private String country;
private String postCode;
}

▶ 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 버전 모두 같은 시간에 수행을 완료했다.

▶ 웹 환경에서 Caching을 이용한 자바 퍼포먼스 개선
웹 환경에서 Object caching 기법은 주로 DB나 파일에서 동일한 내용을 가져오는 루틴에서 사용된다. DBMS에 sql문을 던지고 결과를 받아오는 부분의 내용이 거의 변동이 없을 경우 요청 시마다 매번 sql문을 실행시켜서 결과를 받아오는 것이 아니라 최초 실행된 값을 그대로 반환하는 기법이다. 그리고 시간 Check 기법을 이용해서 특정 시간 경과 후 요청이 들어오면 이전 요청에 의해 수행되어진 값을 갱신해서 반환한다. 결과에 의한 반환값이 메모리에 부담이 되지 않을 정도로 크지 않은 경우, 실시간으로 변경된 정보를 반환값으로 사용하는 루틴이 아닐 경우 유용하게 사용될 수 있다.

▶ Performance CheckList
. 임시로 사용하기 위해 Object를 생성하는 것을 피하라. 특히 Loop에서..
. 빈번하게 호출되는 메소드에서 Object를 생성하는 것을 피하라.
. 가능한 Object를 재사용 하라.
. 임시 Object의 생성을 줄이기 위해 데이타 타입 컨버전 메소드를 재정의 하는 방법을 고 려하라.
. 메소드 설계 시 데이타를 유지하고 있는 Object를 반환하는 메소드보다 데이타로 채워진 재사용 가능한 Object에 접근하는 메소드를 정의하라.
. string이나 Object를 integer 로 대치하라. Object 비교를 위해 equal() 메소드를 호출하지 말고 기본 데이타 타입의 == 연산자를 사용하라.
. 인스턴스 변수로 기본 데이타 타입을 사용하라.
. 단지 메소드 호출을 위해 Object를 생성하는 것을 피하라.
. String 연속 연산자 (+)를 사용하는 것보다 StringBuffer 클래스를 사용하라.
. 복사본을 생성하는 메소드 보다 Object를 직접 수정하는 메소드를 사용하라.
. 생성자는 간단하게… 상속 계층은 얕게…
. 인스턴스 변수를 초기화 하는 것은 한 번 이상 하지 말 것.
. 생성자 호출을 피하기 위해 clone() 메소들 사용할 것.
. 간단한 배열일 경우에는 초기화를.. 복잡한 배열일 경우에는 clone() 메소드 호출.
. 프로그램 내부에서 Object 생성 시기를 조절해서 Object 생성에 따른 bottlenecks를 없앤 다.
. 어플리케이션 내부에서 여분의 시간이 허용된다면 가능한 Object를 빨리 생성하라. 생성 된 Object를 내부적으로 유지하고 있다가 요청이 발생하면 할당하라.
. 사용 가능성이 희박하거나, 분산처리에 의해 Object를 생성할 경우 Object는 생성 시기를 늦춰라.

:
Posted by 라면스프
2008. 10. 28. 17:34

[JAVA] Shell Script 팁 (Unix포함) Enjoy/JAVA2008. 10. 28. 17:34


출처 : http://blog.naver.com/vital45/110024999490


1. 스크립트를 생성할때 구조화를 염두에 둔다
- Unix에서 보통 파일 구조가
/bin
/var/log
등등의 형식을 따르고 있기 때문에 가독성을 높이고 직관적으로 알아볼수 있게 하기 위해 생성

2. find  $PATH -type f | grep -v ".svn"
- .svn이 포함된 디렉토리는 무시하고 file을 검색

3. find . -name "*.sh" | xargs grep "STRING"
- *.sh 의 파일 중 내용에 STRING이 포함되어 있는 곳을 stdout으로 출력

4. 제어권을 갖고 있는 곳은 한군데로 통일하고, 나머지 스크립트는 모든 인자를 받아서
각각의 행동을 하도록 한다.

5. 로그 이름은 script이름과 되도록 통일하여 가독성을 높이도록 한다.

6. wget --output-file=/DIRECTORY/output.log --spider http://localhost:8080/
- wget: 지정된 경로에서 파일 다운로드
- --output-file : 로그 메세지를 지정된 파일에 저장
- --spider: 파일 다운로드 받지 않음

7. shell에서 덧셈 시
- loopcount=0
- loopcount=`expr $loopcount + 1`
- 중요한건 expr 문장을 감싼것은 `(tild)라는 것. 물론 syntax에서 구별할수 있겠지만...

8. svn checkout REPO_PATH PATH
- checkout 명령은 PATH에 없으면 REPO_PATH에서 내려받고, 이미 PATH가 존재하면 update를 한다.
- checkout, update 명령을 굳이 구별할 필요가 없음.

9. shell 스크립트 호출 후 표준출력을 웹 화면에서 보여주는 방법 (jdk 1.5)
(1) jsp 에서
cmd = "ssh";
url = id@server;
args = "A B C";

ProcessBuilder process = ProcessBuilder.start(cmd, url, args);
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);

while ((line = br.readLine()) != null)
   System.out.println(line);

(2) java에서
cmd = "ssh";
url = id@server;
args = "A B C";

ProcessBuilder process = ProcessBuilder.start(cmd, url, args);
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);

while ((line = br.readLine()) != null) {
   // newLine 가공 부분은 따로 처리하는게 있는걸로 알고있는데..
  // 나중에 한번 찾아봐야 할듯. 아래는 예상 코드
   String contents = line + "\n";
}

contents를 jstl을 이용하여 뿌려주는 식으로...

10. Subversion에서의 tag
- SVN Repository를 구성할때 아래와 같은 구조를 권장하고 있다.
repository/trunk : 현재 작업본
repository/branches : 테스트 버전이랄까? (beta버전)
repository/tags : 잠금 버전 (완성본)

TotoiseSVN에서 branches/tags를 클릭하면 실제로 수행하는 명령어는 svn copy다.
svn copy는 Working Copy(이하 WC) 와 Repository PATH(이하 URL)에서 실행할 수 있다. (조합 4가지)
svn copy -m "Message" [WC|URL] [WC|URL]

참고: svn switch 를 실행하면 현재 경로에서부터는 svn repository path를 변경한다.
.svn/entries 의 주소를 확인해보면 알 수 있다.
더 확장하면 svn propset을 이용한 external link를 하는 법도 있는데.. 이건 정확히 잘 모르겠음.

11. argument를 delimeter로 잘라서 loop 돌리는 법
INPUT=$1
DELIM=":" # Delimiter

for part in `echo $input | sed "s/$DELIM/ /g"`
do
   echo $part
done
- sed명령어를 묶어준 것도 역시 `(tild) 이다.

12. ls 에 대한 새삼 깨달은 팁
- ls | grep $option1 | grep $option2
grep 을 줘서 각 인자에 대한 리스트를 뽑아올 수 있다.

 

:
Posted by 라면스프