Assertion

Java/The Java Language 2013. 2. 6. 15:48

Assertion 이란?
Assertion? Programmer
자신이 전개하고 있는 Code 내용에서 Programmer 생각하고 있는 움직임과 그리고 특정 지점에서의 Program상의 설정 값들이 일치하고 있는지를 검사할 있도록 하는 것이 바로 Assertion이다. 예로 어느 특정 method Arguement 값은 10이상이어야 한다는 Programmer 확고함이 있다고 하자! 이럴 Assertion 사용하여 Programmer 주장하는 확고함을 조건으로 명시하고 조건을 만족할 때만 Code 실행할 있도록 하는 것이 Assertion이다. (Assertion이 조건을 만족하여 실행이되면 프로그램이 중단된다)

 

Assertion exception(Exception)의 차이점은?
Exception
특정한 Code에서 exception 발생하므로 일어나는 정상적인 Program 종료와 같은 1차적인 손실을 막고 exception 대한 처리로 인해 Program 신뢰성을 높이는 . 하지만 Assertion 어떤 결과를 위해 특정 Code 변수의 값을 Programmer 예상하는 값이어야 하는 것을 검증하는 것에 차이가 있다

 

Assertion의 문법은 다음 두 가지로 나눈다.
1

assert [boolean];


2

assert [boolean] : [표현식];



Assertion 간단한 예문

assert var>10;

assert var<10 : “10보다 작은 값이어야 !”;

assert str.equals(“”);

assert !str.equals(“”);

assert str != null : “str null값이 들어오면 안됨!”; 

 


Assertion 컴파일 및 실행 법

Java Compiler ‘Assertion’기능을 수행하지 않으면서 Compile 하게 되어 있다. 그러므로 Compiler에게 ‘Assertion’기능을 부여하면서 Compile 수행하도록 하기 위해서는 다음과 같은 옵션을 주어야 한다.

java –source 1.5 [File name]

 

실행 때도 마찬가지로 다음과 같이 옵션을 주면서 실행해야 한다.

java –ea [class name]

 

-ea : Enable Assertions라고 해서 Assertion기능을 사용 가능하게 하는 Option이다.

-da : Disable Assertions라고 해서 ea 반대 Option이다.

 


Assertion 예제

public class AssertTest1 {

 

       public static void main(String[] args) {

 

              assert args.length > 0 : "시작시 인자값이 없습니다.";

              System.out.println(args[0]);

 

              }

}

 

 

[Assertion Case문과 함께 쓰이는 경우]

              switch (direction) {

                     case LEFT:

                           doLeft();

                           break;

                     case RIGHT:

                           doRight();

                           break;

                     default:

                           assert false;

       }

 

출처 : http://zion437.tistory.com/128

 

 

 

'Java > The Java Language' 카테고리의 다른 글

What is Enum in Java  (1) 2013.02.06
Static and Nostatic Initializer blocks  (0) 2013.02.06
Java stack trace with line numbers  (0) 2013.02.06
JVM Architecture  (0) 2013.02.06
JAR file  (0) 2013.02.06
Posted by Steven J.S Min
,

자바 개발자에게는 Exception은 필요하기도 하면서도 귀찮은 존재이죠. 하지만, 디버깅을 위해서라면 최고의 해답이 될수 있다고 생각이 됩니다.

하지만, 특정 경우 StackTrace의 소스 라인번호가 나오지 않고, 단순히 "compiled code"라고 나오면 참으로 답답할 따름이겠죠?

이를 해결 하기 위한 방법은 -Djava.compiler=NONE 을 추가해주거나 환경변수로 JAVA_COMPILER=NONE 해주는 방법입니다.

 

예1) java -Djava.compiler=NONE 클래스명

 

예2) export JAVA_COMPILER=NONE java 클래스명

 

해당 옵션은 정확히는 JIT 옵션을 제거하는 것이긴 하지만, JIT 옵션이 활성화 되어 있는 경우 class 파일을 native 코드로 컴파일 하면서 라인번호가 사라지는 것입니다.

 

하지만, 최근의 IBM java 에서 JIT를 끄고 돌리는 경우 성능상 많은 손실을 볼 수 있습니다.

(경험상으로는 jdk 1.3인 경우는 약 10% 정도의 성능 손실을, 1.4인 경우는 대략 20~30%정도의 성능 손실이 되는 듯합니다.)

 

결론:

1) StackTrace의 라인번호가 나오지 않는 경우에는 JIT 옵션을 제거하세요.

2) JIT 옵션을 제거하는 경우 성능상 손실을 볼수가 있으므로, 주의하세요

 

참조 : http://thunderguy.com/semicolon/2004/02/11/java-stack-trace-with-line-numbers/

 

Sometimes, application servers don’t display the line numbers during printing the trace but instead just print the method name with the “(Unknown Source)” on it.

eg. something like this.

Exception in thread “main” java.lang.ArithmeticException at TraceWithoutLineNumbers.main(Unknown Source)

I wrote this small class to just help me learn why the line numbers get missed and Unknown Source comes up when the trace gets printed.

 

package test;

 

public class TraceWithoutLineNumbers {

 

       public static void main(String[] args) {

              System.out.println("hello world");

 

              if (true) {

                     throw new ArithmeticException();

              }

              System.out.println("very true");

       }

}

 

For the above code, I gave a

Ø  javac TraceWithoutLineNumbers.java

and got

java TraceWithoutLineNumbers hello world Exception in thread “main” java.lang.ArithmeticException at TraceWithoutLineNumbers.main(TraceWithoutLineNumbers.java:9)

 

Cool. Got the line number.

By default, the minimal debugging option is on. (Only line number and source file information is generated). Here is the list of all the other java compiler options.

So, I do a

Ø  javac -g:none TraceWithoutLineNumbers.java

 

java TraceWithoutLineNumbers hello world Exception in thread “main” java.lang.ArithmeticException at TraceWithoutLineNumbers.main(Unknown Source)

 

There is nothing you could do on this if the class is built and run by the server ( read if you dont have control over the class’ compilation). However, if it is a standalone class, you can very well do a javac -g .

Or use this option.

-g:{keyword list}

Generate only some kinds of debugging information, specified by a comma

separated list of keywords. Valid keywords are:

source

 Source file debugging information

lines

 Line number debugging information

vars

 Local variable debugging information

Probably, the app server or the web server you are running your class on would have disabled the debugging option for performance purposes.

 

참조 : http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javac.html#options

'Java > The Java Language' 카테고리의 다른 글

What is Enum in Java  (1) 2013.02.06
Static and Nostatic Initializer blocks  (0) 2013.02.06
Assertion  (1) 2013.02.06
JVM Architecture  (0) 2013.02.06
JAR file  (0) 2013.02.06
Posted by Steven J.S Min
,

JVM Architecture

JVM Class Loader System을 통해 Class파일들을 JVM으로 로딩한다. 로딩된 Class파일들은 Execution Engine을 통해 해석 난 후에 Runtime Data Areas에 적재되어 실질적인 수행이 이루어지게 된다. 이런 과정 속에서 JVM은 필요에 따라 Thread동기화와 GC등의 작업을 수행하게된다

 

Method Area Heap은 모든 Thread와 공유되지만  PC Register, Java Virtial Machine Stack, Native Method Stack Thread 별로 생성된다.

 

Runtime Data Areas

 


Runtime Data Areas JVM Java 프로그램을 수행하기 위해 OS로 부터 할당받는 메모리 영역이라고 정의내릴 수 있다. Runtime Data Areas는 각각의 목적에 따라 PC Register, Java Virtual Machine Stacks, Native Method Stacks, Method Area, Heap 이라는 5개의 영역으로 나뉘게 된다. 이중 PC Register, Java Virtual Machine Stacks, Native Method Stacks은 각 Thread별로 생성되고 Method Area Heap은 모든 Thread에게 공유된다. Java Virtual Machine Stacks, Native Method Stacks 1.3버전 이상의 JVM에서 통합이 되어 있다.

 

PC Registers : Thread별로 연산을 위한 OperandOperation을 임시로 저장하는 버퍼공간

JVM Stacks : Thread 별로 생성되는 공간으로 스레드의 수행정보를 기록하는 메모리 영역

여러 Stack Frame들로 구성 à Stack Frame Local Variable section, Operand Stack, Frame Data로 나뉘어져있으며 Thread Dump를 뜬다는 것은 이 스택 정보를 보는 것이다.

Native Method Stacks : Thread별로 JNI를 수행하기위한 메모리공간

Heap: 클래스에서 생성되는 오브젝트(인스턴스화된 클래스), Array객체만 저장

Method Area : 모든 Thread들이 공유하는 메모리 공간, GC대상이 되는 공간, 흔히 Permant area(Sun) 영역 통칭(Class Object – IBM)

-          Type information, Constant pool, Field information, Method information

-          Class variables, Reference to class <classloader>, reference to class <class>

 

출처 : ‘Java Performance Fundamental, 김한도 지음, 엑셈'

 

 

Java코드 수행 과정

  1.  Class Loadder System을 통해 Class파일들을 JVM으로 로딩한다

  2.  로딩된 Class 파일들은 Execution Engine을 통해 해석된다.

  3.  해석된 프로그램은 Runtime Data Areas에 배치되어 실직적인 수행이 이루어지게 된다이러한 실행 과정 속에서 JVM은 필요에 따라 Thread Synchronization  Garbage Collection 같은 관리작업을 수행하게 된다.

 

 

 

Java Reference와 GC(Soft, Weak, Phantom Reference)

[펌] http://helloworld.naver.com/helloworld/329631, NBP 웹플랫폼개발랩 박세훈


Java 가비지 컬렉터(Garbage Collector) 동작 방식에 따라 매우 다양한 종류가 있지만 공통적으로 크게 다음 2가지 작업을 수행한다고 있습니다.

 

                                 1            (heap) 내의 객체 중에서 가비지(garbage) 찾아낸다.

                                 2            찾아낸 가비지를 처리해서 힙의 메모리를 회수한다.

 

최초의 Java에서는 이들 가비지 컬렉션(Garbage Collection, 이하 GC) 작업에 애플리케이션의 사용자 코드가 관여하지 않도록 구현되어 있었습니다. 그러나 2가지 작업에서 다양한 방법으로 객체를 처리하려는 요구가 있었습니다. 이에 따라 JDK 1.2부터는 java.lang.ref 패키지를 추가해 제한적이나마 사용자 코드와 GC 상호작용할 있게 하고 있습니다.

 

java.lang.ref 패키지는 전형적인 객체 참조인 strong reference 외에도 soft, weak, phantom 3가지의 새로운 참조 방식을 각각의 Reference 클래스로 제공합니다. 3가지 Reference 클래스를 애플리케이션에 사용하면 앞서 설명하였듯이 GC 일정 부분 관여할 있고, LRU(Least Recently Used) 캐시 같이 특별한 작업을 하는 애플리케이션을 쉽게 작성할 있습니다. 이를 위해서는 GC 대해서도 이해해야 아니라, 이들 참조 방식의 동작도 이해할 필요가 있습니다.


GC Reachability

Java GC 객체가 가비지인지 판별하기 위해서 reachability라는 개념을 사용한다. 어떤 객체에 유효한 참조가 있으면 'reachable', 없으면 'unreachable' 구별하고, unreachable 객체를 가비지로 간주해 GC 수행한다. 객체는 여러 다른 객체를 참조하고, 참조된 다른 객체들도 마찬가지로 다른 객체들을 참조할 있으므로 객체들은 참조 사슬을 이룬다. 이런 상황에서 유효한 참조 여부를 파악하려면 항상 유효한 최초의 참조가 있어야 하는데 이를 객체 참조의 root set이라고 한다.

 

JVM에서 메모리 영역인 런타임 데이터 영역(runtime data area) 구조를 그림으로 그리면 다음과 같다.


[그림 1 런타임 데이터 영역(Oracle HotSpot VM 기준)]


런타임 데이터 영역은 위와 같이 스레드가 차지하는 영역들과, 객체를 생성 보관하는 하나의 , 클래스 정보가 차지하는 영역인 메서드 영역, 크게 부분으로 나눌 있다. 그림에서 객체에 대한 참조는 화살표로 표시되어 있다.

 

힙에 있는 객체들에 대한 참조는 다음 4가지 종류 하나이다.

·         내의 다른 객체에 의한 참조

·         Java 스택, Java 메서드 실행 시에 사용하는 지역 변수와 파라미터들에 의한 참조

·         네이티브 스택, JNI(Java Native Interface) 의해 생성된 객체에 대한 참조

·         메서드 영역의 정적 변수에 의한 참조

이들 내의 다른 객체에 의한 참조를 제외한 나머지 3개가 root set으로, reachability 판가름하는 기준이 된다.

 

reachability 자세히 설명하기 위해 root set 내의 객체를 중심으로 다시 그리면 다음과 같다.

[그림 2 Reachable 객체와 Unreachable 객체]


그림에서 보듯, root set으로부터 시작한 참조 사슬에 속한 객체들은 reachable 객체이고, 참조 사슬과 무관한 객체들이 unreachable 객체로 GC 대상이다. 오른쪽 아래 객체처럼 reachable 객체를 참조하더라도, 다른 reachable 객체가 객체를 참조하지 않는다면 객체는 unreachable 객체이다.

 

그림에서 참조는 모두 java.lang.ref 패키지를 사용하지 않은 일반적인 참조이며, 이를 흔히 strong reference 부른다.


Soft, Weak, Phantom Reference

java.lang.ref soft reference weak reference, phantom reference 클래스 형태로 제공한다. 예를 들면, java.lang.ref.WeakReference 클래스는 참조 대상인 객체를 캡슐화(encapsulate) WeakReference 객체를 생성한다. 이렇게 생성된 WeakReference 객체는 다른 객체와 달리 Java GC 특별하게 취급한다(이에 대한 내용은 뒤에서 다룬다). 캡슐화된 내부 객체는 weak reference 의해 참조된다.

 

다음은 WeakReference 클래스가 객체를 생성하는 예이다.

 

WeakReference<Sample> wr = new WeakReference<Sample>( new Sample());
Sample ex = wr.get();
...
ex = null;
 


코드의 번째 줄에서 생성한 WeakReference 클래스의 객체는 new() 메서드로 생성된 Sample 객체를 캡슐화한 객체이다. 참조된 Sample 객체는 번째 줄에서 get() 메서드를 통해 다른 참조에 대입된다. 시점에서는 WeakReference 객체 내의 참조와 ex 참조, 개의 참조가 처음 생성한 Sample 객체를 가리킨다.

[그림 3 Weak Reference 1]


코드의 마지막 줄에서 ex 참조에 null 대입하면 처음 생성한 Sample 객체는 오직 WeakReference 내부에서만 참조된다. 상태의 객체를 weakly reachable 객체라고 하는데, 이에 대한 자세한 내용은 뒤에서 다룬다.

[그림 4 Weak Reference 2]


Java 스펙에서는 SoftReference, WeakReference, PhantomReference 3가지 클래스에 의해 생성된 객체를 "reference object"라고 부른다. 이는 흔히 strong reference 표현되는 일반적인 참조나 다른 클래스의 객체와는 달리 3가지 Reference 클래스의 객체에 대해서만 사용하는 용어이다. 또한 이들 reference object 의해 참조된 객체는 "referent"라고 부른다. Java 스펙 문서를 참조할 이들 용어를 명확히 알면 이해하기 쉽다. 위의 소스 코드에서 new WeakReference() 생성자로 생성된 객체는 reference object이고, new Sample() 생성자로 생성된 객체는 referent이다.


Reference Reachability

앞에서 설명한 것처럼, 원래 GC 대상 여부는 reachable인가 unreachable인가로만 구분하였고 이를 사용자 코드에서는 관여할 없었다. 그러나 java.lang.ref 패키지를 이용하여 reachable 객체들을 strongly reachable, softly reachable, weakly reachable, phantomly reachable 자세히 구별하여 GC 때의 동작을 다르게 지정할 있게 되었다. 다시 말해, GC 대상 여부를 판별하는 부분에 사용자 코드가 개입할 있게 되었다.

 

번째 그림에서 몇몇 객체들을 WeakReference 바꾸어서 예를 들어보면 다음과 같다.

[그림 5 Reachable, Unreachable, Weakly Reachable 예제]


녹색으로 표시한 중간의 객체는 WeakReference로만 참조된 weakly reachable 객체이고, 파란색 객체는 strongly reachable 객체이다. GC 동작할 , unreachable 객체뿐만 아니라 weakly reachable 객체도 가비지 객체로 간주되어 메모리에서 회수된다. root set으로부터 시작된 참조 사슬에 포함되어 있음에도 불구하고 GC 동작할 회수되므로, 참조는 가능하지만 반드시 항상 유효할 필요는 없는 LRU 캐시와 같은 임시 객체들을 저장하는 구조를 쉽게 만들 있다.

 

그림에서 WeakReference 객체 자체는 weakly reachable 객체가 아니라 strongly reachable 객체이다. 또한, 그림에서 A 표시한 객체와 같이 WeakReference 의해 참조되고 있으면서 동시에 root set에서 시작한 참조 사슬에 포함되어 있는 경우에는 weakly reachable 객체가 아니라 strongly reachable 객체이다.

 

GC 동작하여 어떤 객체를 weakly reachable 객체로 판명하면, GC WeakReference 객체에 있는 weakly reachable 객체에 대한 참조를 null 설정한다. 이에 따라 weakly reachable 객체는 unreachable 객체와 마찬가지 상태가 되고, 가비지로 판명된 다른 객체들과 함께 메모리 회수 대상이 된다.


Strengths of Reachability


앞에서 설명한 것처럼 reachability 5종류가 있고 이는 GC 객체를 처리하는 기준이 된다. Java 스펙에서는 이들 5종류의 reachability "Strengths of Reachability" 부른다. 앞의 예제 그림에서는 weakly reachable 예를 들었기 때문에 WeakReference 표시하였으나, SoftReference, PhantomReference 등을 이용하여 여러 가지 방식으로 reachability 지정할 있고 이에 따라 객체들의 GC 여부는 다양하게 달라지게 된다. 하나의 객체에 대한 참조의 개수나 참조 형태에는 아무런 제한이 없으므로, 하나의 객체는 여러 strong reference, soft reference, weak reference, phantom reference 다양한 조합으로 참조될 있다.

 

Java GC root set으로부터 시작해서 객체에 대한 모든 경로를 탐색하고 경로에 있는 reference object들을 조사하여 객체에 대한 reachability 결정한다. 다양한 참조 관계의 결과, 하나의 객체는 다음 5가지 reachability 하나가 있다.

 

  • strongly reachable: root set으로부터 시작해서 어떤 reference object 중간에 끼지 않은 상태로 참조 가능한 객체다시 말해, 객체까지 도달하는 여러 참조 사슬 reference object 없는 사슬이 하나라도 있는 객체
  • softly reachable: strongly reachable 객체가 아닌 객체 중에서 weak reference, phantom reference 없이 soft reference 통과하는 참조 사슬이 하나라도 있는 객체
  • weakly reachable: strongly reachable 객체도 softly reachable 객체도 아닌 객체 중에서, phantom reference 없이 weak reference 통과하는 참조 사슬이 하나라도 있는 객체
  • phantomly reachable: strongly reachable 객체, softly reachable 객체, weakly reachable 객체 모두 해당되지 않는 객체 객체는 파이널라이즈(finalize)되었지만 아직 메모리가 회수되지 않은 상태이다.
  • unreachable: root set으로부터 시작되는 참조 사슬로 참조되지 않는 객체


다음 예의 경우 객체 B reachability softly reachable이다.

[그림 6 Softly Reachable]


root set으로부터 바로 SoftReference 통해서 B 참조할 있기 때문이다. 만약 root set SoftReference 대한 참조가 없다면(, 왼쪽 아래 화살표를 삭제한다면), 객체 B phantomly reachable 된다.



Softly Reachable SoftReference

softly reachable 객체, strong reachable 아니면서 오직 SoftReferencce 객체로만 참조된 객체는 힙에 남아 있는 메모리의 크기와 해당 객체의 사용 빈도에 따라 GC 여부가 결정된다. 그래서 softly reachable 객체는 weakly reachable 객체와는 달리 GC 동작할 때마다 회수되지 않으며 자주 사용될수록 오래 살아남게 된다. Oracle HotSpot VM에서는 softly reachable 객체의 GC 조절하기 위해 다음 JVM 옵션을 제공한다.

 

-XX:SoftRefLRUPolicyMSPerMB=<N>

 

옵션의 기본값은 1000이다.

softly reachable 객체의 GC 여부는 옵션의 <N> 설정한 숫자에 따라 다음 수식에 의해 결정된다.

 

(마지막 strong reference GC 때로부터 지금까지의 시간) > (옵션 설정값 N) * (힙에 남아있는 메모리 크기)

 

어떤 객체가 사용된다는 것은 strong reference 의해 참조되는 것이므로 수식의 좌변은 해당 객체가 얼마나 자주 사용되는지를 의미한다. 옵션 설정값이 1000이고 남아 있는 메모리가 100MB이면, 수식의 우변은 1,000ms/MB * 100MB = 100,000ms = 100sec, 100초가 된다(옵션 이름 마지막이 MSPerMB 끝나므로 옵션 설정값의 단위는 ms/MB임을 있다). 따라서 softly reachable 객체가 100 이상 사용되지 않으면 GC 의해 회수 대상이 된다. 힙에 남아있는 메모리가 작을수록 우변의 값이 작아지므로, 힙이 거의 소진되면 대부분의 softly reachable 객체는 모두 메모리에서 회수되어 OutOfMemoryError 막게 것이다.

 

softly reachable 객체를 GC하기로 결정되면 앞서 설명한 WeakReference 경우와 마찬가지로 참조 사슬에 존재하는 SoftReference 객체 내의 softly reachable 객체에 대한 참조가 null 설정되며, 이후 softly reachable객체는 unreachable 객체와 마찬가지가 되어 GC의해 메모리가 회수된다.


Weakly Reachable WeakReference

weakly reachable 객체는 특별한 정책에 의해 GC 여부가 결정되는 softly reachable 객체와는 달리 GC 수행할 때마다 회수 대상이 된다. 앞서 설명한 것처럼 WeakReference 내의 참조가 null 설정되고 weakly reachable 객체는 unreachable 객체와 마찬가지 상태가 되어 GC 의해 메모리가 회수된다. 그러나 GC 실제로 언제 객체를 회수할지는 GC 알고리즘에 따라 모두 다르므로, GC 수행될 때마다 반드시 메모리까지 회수된다고 보장하지는 않는다. 이는 softly reachable 객체는 물론 unreachable 객체도 마찬가지이다. GC GC 대상인 객체를 찾는 작업과 GC 대상인 객체를 처리하여 메모리를 회수하는 작업은 즉각적인 연속 작업이 아니며, GC 대상 객체의 메모리를 번에 모두 회수하지도 않는다.


LRU 캐시와 같은 애플리케이션에서는 softly reachable 객체보다는 weakly reachable 객체가 유리하므로 LRU 캐시를 구현할 때에는 대체로 WeakReference 사용한다. softly reachable 객체는 힙에 남아 있는 메모리가 많을수록 회수 가능성이 낮기 때문에, 다른 비즈니스 로직 객체들을 위해 어느 정도 비워두어야 공간이 softly reachable 객체에 의해 일정 부분 점유된다. 따라서 전체 메모리 사용량이 높아지고 GC 자주 일어나며 GC 걸리는 시간도 상대적으로 길어지는 문제가 있다.


ReferenceQueue

phantomly reachable 객체의 동작과 PhantomReference 설명하기 전에 java.lang.ref 패키지에서 제공하는 ReferenceQueue 클래스에 대해 설명할 필요가 있다.

 

SoftReference 객체나 WeakReference 객체가 참조하는 객체가 GC 대상이 되면 SoftReference 객체, WeakReference 객체 내의 참조는 null 설정되고 SoftReference 객체, WeakReference 객체 자체는 ReferenceQueue enqueue된다. ReferenceQueue enqueue하는 작업은 GC 의해 자동으로 수행된다. ReferenceQueue poll() 메서드나 remove() 메서드를 이용해 ReferenceQueue 이들 reference object enqueue되었는지 확인하면 softly reachable 객체나 weakly reachable 객체가 GC되었는지를 파악할 있고, 이에 따라 관련된 리소스나 객체에 대한 후처리 작업을 있다. 어떤 객체가 이상 필요 없게 되었을 관련된 후처리를 해야 하는 애플리케이션에서  ReferenceQueue 유용하게 사용할 있다. Java Collections 클래스 중에서 간단한 캐시를 구현하는 용도로 자주 사용되는 WeakHashMap 클래스는 ReferenceQueue WeakReference 사용하여 구현되어 있다.

 

SoftReference WeakReference ReferenceQueue 사용할 수도 있고 사용하지 않을 수도 있다. 이는 이들 클래스의 생성자 중에서 ReferenceQueue 인자로 받는 생성자를 사용하느냐 아니냐로 결정한다. 그러나 PhantomReference 반드시 ReferenceQueue 사용해야만 한다. PhantomReference 생성자는 하나이며 항상 ReferenceQueue 인자로 받는다.

 

ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
PhantomReference<Object> pr = new PhantomReference<Object>(referent, rq);

 

SoftReference, WeakReference 객체 내부의 참조가 null 설정된 이후에 ReferenceQueue enqueue되지만, PhantomReference 객체 내부의 참조를 null 설정하지 않고 참조된 객체를 phantomly reachable 객체로 만든 이후에 ReferenceQueue enqueue된다. 이를 통해 애플리케이션은 객체의 파이널라이즈 이후에 필요한 작업들을 처리할 있게 된다. 자세한 내용은 다음 절에서 설명한다.


Phantomly Reachable PhantomReference

softly reachable weakly reachable, phantomly reachable 많이 다르다. 이를 설명하기 위해서는 먼저 GC 동작을 설명해야 한다. GC 대상 객체를 찾는 작업과 GC 대상 객체를 처리하는 작업이 연속적이지 듯이, GC 대상 객체를 처리하는 작업과 할당된 메모리를 회수하는 작업도 연속된 작업이 아니다. GC 대상 객체를 처리하는 작업, 객체의 파이널라이즈 작업이 이루어진 후에 GC 알고리즘에 따라 할당된 메모리를 회수한다.

GC 대상 여부를 결정하는 부분에 관여하는 softly reachable, weakly reachable과는 달리, phantomly reachable 파이널라이즈와 메모리 회수 사이에 관여한다. strongly reachable, softly reachable, weakly reachable 해당하지 않고 PhantomReference로만 참조되는 객체는 먼저 파이널라이즈된 이후에 phantomly reachable 간주된다. 다시 말해, 객체에 대한 참조가 PhantomReference 남게 되면 해당 객체는 바로 파이널라이즈된다. GC 객체를 처리하는 순서는 항상 다음과 같다.


1.       soft references

2.       weak references

3.       파이널라이즈

4.       phantom references

5.       메모리 회수


, 어떤 객체에 대해 GC 여부를 판별하는 작업은 객체의 reachability strongly, softly, weakly 순서로 먼저 판별하고, 모두 아니면 phantomly reachable 여부를 판별하기 전에 파이널라이즈를 진행한다. 그리고 대상 객체를 참조하는 PhantomReference 있다면 phantomly reachable 간주하여 PhantomReference ReferenceQueue 넣고 파이널라이즈 이후 작업을 애플리케이션이 수행하게 하고 메모리 회수는 지연시킨다.

 

앞서 설명한 것처럼 PhatomReference 항상 ReferenceQueue 필요로 한다. 그리고 PhantomReference get() 메서드는 SoftReference, WeakReference 달리 항상 null 반환한다. 따라서 phantomly reachable 판명된 객체는 이상 사용될 없게 된다. 그리고 phantomly reachable 판명된 객체에 대한 참조를 GC 자동으로 null 설정하지 않으므로, 후처리 작업 후에 사용자 코드에서 명시적으로 clear() 메서드를 실행하여 null 설정해야 메모리 회수가 진행된다.

 

이와 같이, PhantomReference 사용하면 어떤 객체가 파이널라이즈된 이후에 할당된 메모리가 회수되는 시점에 사용자 코드가 관여할 있게 된다. 파이널라이즈 이후에 처리해야 하는 리소스 정리 등의 작업이 있다면 유용하게 사용할 있다. 그러나 개인적으로는 PhantomReference 사용하는 코드를 거의 적이 없으며, 효용성에 대해서는 의문이 있다.

 

마치며

Java Reference 선후 관계와 용어가 복잡해서 글로 쉽게 풀어쓰기가 어려워 본문이 장황해졌다. 본문의 내용을 간단히 요약하면 다음과 같다.

·         Java GC GC 대상 객체를 찾고, 대상 객체를 처리(finalization)하고, 할당된 메모리를 회수하는 작업으로 구성된다.

·         애플리케이션은 사용자 코드에서 객체의 reachability 조절하여 Java GC 일부 관여할 있다.

·         객체의 reachability 조절하기 위해서 java.lang.ref 패키지의 SoftReference, WeakReference, PhantomReference, ReferenceQueue 등을 사용한다.


개인적으로는 내부 캐시 등을 구현하고자 하는 대부분의 애플리케이션에서는 WeakReference 혹은 이를 이용한 WeakHashMap만으로도 충분하다고 생각한다. 다른 애플리케이션에서는 가끔 SoftReference 사용하는 경우도 있지만, PhantomReference 거의 예제가 없으며 그만큼 불필요할 것이다. 이들 Java Reference들과 관련된 GC 동작을 이해하면 Java heap 메모리 문제에서 더욱 유연한 애플리케이션 작성에 크게 도움이 것이다

 

 

JVM Parameters

On the basis of how we specify JVM option it can be divided into two parts, JVM Options which starts with –X and those which starts with -XX:

1)    JVM Options that begin with -X are non-standard (thy are not guaranteed to be supported on all JVM implementations), and are subject to change without notice in subsequent releases of the JDK.

2)    JVM Options or parameters which are specified with -XX are not stable and are not recommended for casual use. These options are subject to change without notice also


I was thinking about writing post on JVM options when I completed my post on Java Heap Size and Java Garbage Collection because these are two main area where we see usages of various JVM flags. But it didn’t happened even after I covered OutOfMemoryError post which has some JVM option to solve OutOfMemoryError in Java. Now I am happy that I have completed this piece of information and its ready to be published. As always I look for your feedback, suggestions and any other JVM flags which I have missed and you guys find useful to share. 

Good knowledge of JVM options specially related to GC tuning is important for time critical application e.g. high volume low latency electronic trading platform where every micro seconds matter. though getting right combination requires lot of profiling and trial and error and depends heavily on nature of trading application.

 

Important Points about JVM Options:

1)    Boolean JVM options can be  turned on with -XX:+ and can be turned off with -XX:-.

2)    Numeric JVM Options can be set with -XX:=. Numbers can include 'm' or 'M' for megabytes, 'k' or 'K' for kilobytes, and 'g' or 'G' for gigabytes (for example, 32k is the same as 32768).

3)    String JVM options can be set by using -XX:=, and usually used to specify a file, a path, or a list of commands.

 

The command java -help lists the standard options (standard across different JVM implementations) for the Java application launcher. The command java -X can be used to see the Java application launcher's non-standard (X for extension specific to that JVM) arguments.The -X options are non-standard and subject to change without notice. If you wish to detect which JVM arguments your currently running Java application is using, you can use the ManagementFactory.getRuntimeMXBean().getInputArguments()

 

Now here is my list of important JVM flags, switches, options or parameters which is most commonly used while running Java applications:

 

1) JVM memory options related to java heap size

Following three JVM options are used to specify initial and max heap size and thread stack size while running Java programs.

 -Xms        set initial Java heap size

 -Xmx        set maximum Java heap size

 -Xss>         set java thread stack size

 

2) JVM option to print gc details

-verbose:gc logs garbage collector runs and how long they're taking. I generally use this as my first tool to investigate if GC is a bottleneck for a given application.

 

-XX:+PrintGCDetails includes the data from -verbose:gc but also adds information about the size of the new generation and more accurate timings.

 

-XX:-PrintGCTimeStamps  Print timestamps at garbage collection.

 

3) JVM parameters to specify Java Garbage collector

-XX:+UseParallelGC      Use parallel garbage collection for scavenges

-XX:-UseConcMarkSweepGC Use concurrent mark-sweep collection for the old generation. (Introduced in 1.4.1)

-XX:-UseSerialGC        Use serial garbage collection. (Introduced in 5.0.)

beware when you use GC Parameters if you are working on time critical application e.g. high frequency trading application. As  GC is time consuming operation and its desired to create a balance.

 

4) JVM debug options JVM options for remote debugging

-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
to read more about remote debugging check How to Setup Java remote debugging in Eclipse and 10 Java debugging tips in Eclipse 

 

5) JVM options related to profiling

-Xprof

-Xrunhprof

 

6) JVM options related to java classpath

Xbootclasspath specifies classpath entries you want loaded without verification. The JVM verifies all classes it loads to ensure they don't try to dereference an object with an int, pop extra entries off the stack or push too many, and so on. This verification is part of the reason why the JVM is very stable, but it's also rather costly, and responsible for a large part of start up delay. Putting classes on the bootclasspath skips this cost, but should only be used when you know the classes have been verified many times before. In JRuby, this reduced startup time by half or more for a simple script. The -Xbootclasspath option can be used to either prepend (/p) or append (/a) resources to the bootstrap classpath. You Can read more about Java Classpath in my articles How Classpath Works in Java and How to Solve ClassNotFoundException in Java

 

7) JVM options to change  Perm Gen Size

These JVM optiosn are quite useful to solve java.lang.OutOfMemoryError:Perm Gen Space.

-XX:PermSize and MaxPermSize

-XX:NewRatio=2  Ratio of new/old generation sizes.

-XX:MaxPermSize=64m     Size of the Permanent Generation.

 

8) JVM parameters to trace classloading and unloading

-XX:+TraceClassLoading and -XX:+TraceClassUnloading are two JVM options which we use to print logging information whenever classes loads into JVM or unloads from JVM. These JVM flags are extremely useful if you have any memory leak related to classloader and or suspecting that classes are not unloading or garbage collected.

9) JVM switches related to logging

-XX:+TraceClassLoading and -XX:+TraceClassUnloading print information class loads and unloads. Useful for investigating if you have a class leak or if old classes (like JITed Ruby methods in JRuby) are getting collected or not. You can read more about logging in Java on my post 10 Tips while logging in Java

-XX:+PrintCompilation prints out the name of each Java method Hotspot decides to JIT compile. The list will usually show a bunch of core Java class methods initially, and then turn to methods in your application. In JRuby, it eventually starts to show Ruby methods as well

10) JVM Switches for debugging purpose

-XX:HeapDumpPath=./java_pid.hprof  Path to directory or file name for heap dump.

-XX:-PrintConcurrentLocks       Print java.util.concurrent locks in Ctrl-Break thread dump.

-XX:-PrintCommandLineFlags   Print flags that appeared on the command line.

That’s all on JVM Options, I understand its not possible to remember all JVM flags but at-least having an idea of what kind of JVM flags are available is good asset. Image for JVM parameters is from Java tuning and Nutshell.  For full list of JVM options you can refer these link from Oracle Java site: Java Hotspot VM Options

Read more: http://javarevisited.blogspot.com/2011/11/hotspot-jvm-options-java-examples.html#ixzz2K5m4bLjJ

 

출처 : http://javarevisited.blogspot.sg/2011/11/hotspot-jvm-options-java-examples.html

 

 

 

More Detail Configuring a JVM Heap

There are a lot of arguments for setting a JVM Heap. But which one we have to use to make it work?

 

On this page you will find which arguments you have to use to configure a JVM to work. I only mention the most important ones, because there is a huge list on arguments for a JVM.

 

Here is a small drawing I made some time ago to make it visual (Clickable, opens a new window):

 

 

You see that it is divided in 3 main parts:

·         Young Generation

·         Old Generations

·         Permanent Generation

 

Let’s start with the first 2 parameters:
-Xms: This will set the lowest size of the Heap.
-Xmx: This will set the maximum size of the Heap.

 

When both parameters have different values, like: -Xms256M -Xmx512M The Heap size will start at 256M and when needed it will grow to a maximum of 512 (The yellow “Virtual” part) M. So the JVM is smart enough to grow if needed and when a GC has run, it will reduce the size to something lower, probably 256M again. But this “smartness” will cost some overhead, so I would advise to set both parameters to the same value like 512M.

 

The -Xms and -Xmx will set the size of the heap, but what is the heap? When you talk about the Heap size, this will only be the total memory of the Young and the Old/Tenured Generations together. How can we control this, so that we can assign a specific part of the Heap size to the Young Generation (Or the Old/Tenured Generation)

 

-XX:NewSize: Like the -Xms, but sets the minimum size for the Young Generation.
-XX:MaxNewSize / -Xmn: Like the -Xmx, but sets the maximum size for the Young Generation.

 

With the next example we set the Young Generation for a minimum and  maximum of 128M:
-Xms512M -Xmx512M -XX:NewSize=128M -XX:MaxNewSize=128M

 

With the above example, 128M of the 512M is for the Young Generation, a fourth of the Heap. You can set it to everything else, but it has to be lower then the 512M which was set by -Xmx.

 

So what it the ideal configuration?
That is really difficult, because each app is different and the usage of the app will always be different for other people. So if customer A has a fourth reserverd for the Young Generation, customer B maybe needs a third of the Heap size for the Young Generation. So the only way to find out what is best for you is to test it with different kinds of sizes. A good starting point is a fourth of the total heap size for the Young Generation.

 

-XX:PermSize: This will set the minimum size of the Permanent Generation
-XX:MaxPermSize: This will set the maximum size of the Permanent Generation

 

Like with the -Xms and -Xmx, if you set the -XX:PermSize with a lowe value then -XX:MaxPermSize, it will grow if it needs more memory. Also with this, I advise you to set the same values for both arguments

 

출처 : http://www.dj-wasabi.nl/java/configuring-a-jvm-heap/


'Java > The Java Language' 카테고리의 다른 글

What is Enum in Java  (1) 2013.02.06
Static and Nostatic Initializer blocks  (0) 2013.02.06
Assertion  (1) 2013.02.06
Java stack trace with line numbers  (0) 2013.02.06
JAR file  (0) 2013.02.06
Posted by Steven J.S Min
,

JAR file

Java/The Java Language 2013. 2. 6. 07:48

Basic

jar 파일은 자바에서 제공되는 압축파일이다. 그러나 단순한 압축 파일이 아닌 라이브러리 처럼 사용되기하고 실행파일 처럼 사용되기도 한다. 그리고 RMI에서는 다른 용도로 사용되기도한다. 이처럼 jar파일은 자바에 있어서 아주 활용도가 높다.

그럼 자신이 만든 프로젝트를 jar 파일로 만들어보자
.

여기서 다음과 같은 프로젝트를 사용하기로 하자
.
패키지는 logging이고 안에 LogServer 클래스가 있고, 클래스는 log4j 사용한다고 가정하자.

 

Compress

가장 단순한 jar파일을 만들고 압축 해제하는 방법을 간단히 보도록 하죠.
다음 두가지 파일이 있다고 하자
.

bin\logging\LogServer.class
bin\env.property


현재 bin 디렉토리 위치에 있다.

jar cvf LogServer.jar env.property logging\

옵션 "c" 압축 옵션이고, "v" 작업 상황을 출력하는 옵션이며, "f" 압축되는 파일 이름을 의미한다. "v" 없어도 화면에 출력이 되지 않을뿐 사용하는데는 지장이 없다.

 


앞에서 bin디렉토리 안으로 이동한 이유는 자바에서 패키지는 디렉토리 단위로 구성이 된다. 다음과 같이 압축을 하면 bin디렉토리까지 포함되어서 제대로된 경로를 찾기 힘들어진다.

ex) jar cvf LogServer.jar bin\env.property bin\logging\


그래서 bin 디렉토리로 먼저 이동한 후에 압축을 하였다.

보기
jar tf LogServer.jar

"t"
압축 파일에 들어있는 목록을 말하며, "f" 압축파일을 입력받겠다는 의미이다.

 

 옵션 "f" 필요한지 의문이 것이다. 도스(DOS)창에서 데이터를 입력 받고 출력 받는 방법은 다양하다. 출력은 유일하게 파일만 있는 것이 아니기 때문이다. 많이 사용하는 중에 하나가 파이프(pipe)라는게 있다. 보통 기호로 "|"형태로 표시하는데 이는 프로그램 간에 데이터 이동 통로라고 생각하면 된다. 혹은 리다이렉트(redirect)라고 ">"라는 기호를 사용하기도 한다. 리다이렉트는 출력을 의미한다고 생각하면 된다. 파일로 출력, 프린트로 출력 . 그러나 도스에서는 이렇게 까지는 사용하지 않고 유닉스 계열에서 많이 사용한다.
참고로 재미나마 다음과 같이 사용할 있다.

 

ex)
jar cv env.property logging\ > test.jar
type test.jar | jar t


Extract

먼저 LogServer.jar tmp 임시 폴더로 복사한다.

jar xvf LogServer.jar

옵션 "x" 압축을 풀겠다는 의미이고 "v" 작업 과정을 표시하는 것이고, "f" 압축을 파일을 지정하겠다는 의미이다. 실제 압축을 내용을 보면 내가 추가하지 않은 파일도 보인다. META-INF 디렉토리에 있는 MANIFEST.MF파일이다. 이는 jar 자동으로 생성되며 단순히 현재 버전 정보만 들어가 있다. 이들의 특별한 역활은 아래에서 보도록 하겠다.

 

 이렇게 해서 대충 jar파일을 압축하고 푸는 것을 보았다. 실제 jar에는 다른 여러 옵션들이 있다. 이는 아래에 친철히(?) 표시해주었다.

 

 


ADD FILE TO JAR

이외에 압축을 새로 만들지 않고 파일만 추가하거나 변경도 가능하다. 이때 사용하는 옵션이 "u"이다. 그리고 압축 없이 저장용으로만 사용할수도 있다. 보통 저장용으로는 실행하는데 빠르며, 압축용은 통신으로 전송할때 빠르다. 본인이 직접 확인해보고 테스트하고 확인해보길 바란다.

이렇게 생성된 jar파일을 클래스 패스에 지정해서 jar파일 안에 있는 클래스를 사용할 있게 된다. 자신만의 다양한 라이브러리를 만들어서 사용할 있다
.

ADVANCED

앞에서 건너뛰 MANIFEST.MF파일에 대해서 자세히 보도록 하겠다.

[
기본정보설정]

 

두가지 항목이 보인다. Manifest-Version Created-By이다. 전자는 현재 파일에 대한 버전 정보를 의미하고 후자는 생성에 대한 현재 프로젝트 버전이나 생성 일자에 대한 정보를 말한다. 내용을 보면 일정한 형식이 있을을 확인 있다. , 가운데 콜론(":") 두고 헤더와 값이 있다.

형식-> 헤더:


헤더와 콜론 사이에는 공배는 없다.
그럼, 입맞에 맞게 manifest.mf파일을 작성해보자. 본인은 다음과 같이 작성하였다. Manifest-Version 그대로 유지하고 Created-By 수정하였다.

 

그럼 jar 압축시 작성한 manifest.mf 사용하는 것을 보겠다. 당연히 디렉토리는 bin으로 이동한다.

jar cvfm LogServer.jar manifest.mf env.property logging\

앞에서 압축하는 방법과 같지만 추가된 옵션 "m" 있다. 이는 사용자가 작성한 manifest.mf파일을 사용하겠다는 의미이다. 여기서는 manifest.mf라는 이름을 사용했지만, 본인이 편한 이름으로 사용할 있다. 저는 jar 있는 파일명과 일치시켰서 사용할뿐이다.

 

실제 압축을 풀어서 내용을 확인해보자.



[
CLASSPATH]

그럼 조금 속도로를 내보자. 이번에는 jar파일 안에 jar파일을 추가하는 경우를 보자. 일단 설명을 하자면, jar파일안에 jar파일을 추가해서 압축을 하면 보통 추가된 jar파일은 인식을 하지 못한다. CLASSPATH 통하지 않는다.(이건 확인안해봐서 모르겠다. .;)
이때 필요한것이 manifest.mf파일 안에 "Class-Path:"라는 헤더이다. 단순히 manifest.mf 작성해서 다음과 같이 사용하면 된다
.
"
중요한 Class-Path 위치이다."

중요! 그렇지 않으면 Class-Path 제대로 적용이 안된다. 추가되는 jar파일 간에는 공배만 추가하면된다. 쉼표(",") 세미콜론(";")등은 사용할 없다.

 


디렉토리 위치는 LogSever 프로젝트 루트로 이동하다 사용하는 LogServer.jar파일은 루트로 복사하고, log4j-1.2.15.jar lib디렉토리에 복사해둔다. 그리고 manifest.mf 작성해주고 env.propery 이동해둔다.

...\Workspace\LogServer\LogServer.jar
...\Workspace\LogServer\lib\log4j-1.2.15.jar
...\Workspace\LogServer\manifest.mf
...\Workspace\LogServer\env.property

LogServer.jar
새로 작성한다. env.property 빠졌다
.
jar cvf LogServer.jar logging\

Jar
파일을 포함한 최종 파일은 LogPack.jar파일이다
.
jar cvfm LogPack.jar manifest.mf env.property LogServer.jar lib\lib4j-1.2.15.jar

실제 실행은 아래와 같으니 참고하길 바란다.

 

 


JAR실행

이번에는 Jar파일을 실행해보자. 당연히 압축된 jar파일 안에는 main 함수가 들어간 클래스가 있어야만 한다. 없다면 백날해도 실행안된다. 절대 안된다.

앞에 예제에서 이런 클래스가 LogServer이다. 압에서 jar파일 안에 jar파일이 존재할 있다고 했다. 안에 있는 jar 파일에 LogServer같은 클래스가 있어도 된다. 단지 manifest.mf 파인 안에 있는 클래스패스 항목에 해당 jar파일을 지정해주면 된다. 이는 앞에 내용을 참조하길 바란다
.

이것 역시
manifest.mf 파일 안에 넣어주면 된다. 입력되는 형식은 아래와 같다.

Main-Class:
패키지이름/클래스이름


LogServer
클래스가 속하는 패키지이름을 넣어주면 된다. , "logging/LogServer"이다.
아래 그림이 manifest.mf 추가된 내용을 보여주고 있다. 이것 역시 위치에 주의해야 한다. 밑에 추가해서 삽입을 하니 해당 내용이 제대로 들어가지 않은다. 만약에 manifest.mf 내용을 입력했는데 해당 내용이 jar파일에 적용되지 않는다면, 위치를 변경하길 바란다. 그런건지 이유는 모르겠다.

           

다시 패키징해서 실행하는 과정은 다음과 같다.

 

 
현재는 간단한 예제만 보았지만, 이렇게 하면 복잡한 응용프로그램도 배포버전으로 배포할 있겠다.

앞의 버전은 기존 라이브러리까지 포함해서 패키징 했다. 이렇게 되었을 경우는 프로그램 일부 변경시 전체 프로그램을 다시 패키징해서 배포해줘야하는 문제점이 있다. 작은 프로그램인 경우는 파일 하나만 있기에 간단히 이동, 복사가 가능하는 간편함이 있다. 그러나 커다란 프로그램인 경우는 이렇게 하기 힘들다. 특히 수정후 배포가 가장 힘들다. 이런 경우는 거의 고정된 jar파일들을 하나로 묶고 수시로 변경되는 파일들은 다른 jar파일로 패키징해서 관리하면 좋다
.
, 기존 프래임워크에 사용하는 라이브러리 등은 고정된 jar파일에 넣어두고, 현재 새로 만드는 소스만 따로 jar파일로 묶어서 배포하는 것이다. 물론 크기에 따라서 분류를 다르게해야되지만...


SEALING

마지막 실링(Sealing) 보겠다. 그다지 많이 사용하지 않지만 일단은 알아두면 사용할데가 있을 같아서 넣어두었다. 실링(Sealing) 지정된 패키지에 모든 클래스는 현재 압축되는 jar파일에 존재해야 한다. 만약 다른 jar파일로 존재하는 경우 사용할 없다.

다시 말해서 내가 LogServer 클래스를 가지는 jar파일을 만들었다. 그런데 logging/Test 패키지에 있는 모든 항목은 LogServer 클래스가 있는 jar파일 내부에 같이 두려고 한다
.
이렇게 하는 이유는 여러 jar파일들 중에서 logging/Test에서 사용할 LogTest클래스가 이전 클래스 패스 상에서 jar파일에 있다면, 해당 LogTest클래스를 실행한다. , 내가 원하는 클래스가 동작하지 않는다. 앞의 설명이 정확한지는 확인해보지 않았지만, 대충 이런 느낌일 것이다
.

이는 ibm.com 내용에 따르면
,

"
패키지 작성자는 패키지된 클래스들의 버전 영속성을 강화할 있다. 봉합은 보안 조치도 제공하여 코드 탬퍼링을 탐지한다.
"

어려운 단어를 써서 쉽게 감이 잡히지 않는다. .
;

이것 역시 manifest.mf 설정되며 형식은 아래와 같다
.

Name:
패키지경로/패키지명
/
Sealed: true

Sealed
항목이 true 되어 있으면 된다. 그러면 해당 "패키지명" 현재 jar파일 안에 포함되어 있어야 한다. 그렇다고 해서 jar 자동으로 패키지를 포함해주지는 않은다. 사용자가 직접 입력해줘야한다. 입력된 내용은 아래 처럼 있다.

 


실행은 살펴보지 않겠다. 필요하신 분은 직접 시도해보시길 바란다. ^^;

기타

다른 것으로는 패키지 정보를 추가하는 부분이다. 반드시 필요한 항목은 아니며 패키지 관리에 도움이 되는 부분이라 있다. manifest.mf 추가되며 형식은 아래와 같다.

Name: 패키지경로/패키지명
/
Specification-Title: 패키지명

Specification-Version:
패키지 버전
Specification-Vendor:
패키지 제조 회사
Implementation-Title:
구현 패키지명
Implementation-Version:
구현버전(보통 빌드번호)
Implementation-Vendor:
구현 제조 회사

아래는 log4j manifest.mf파일 내용이므로 참고하길 바란다.

 

그다지 어려운 내용이 없기 때문에 그냥 넘어가겠다. 단지 스팩 만드는 부분과 실제 구현하는 부분이 분리되어서 추가되었다.


SECURITY
이번에는 좀더 어려운 부분으로 가보자. 이번에는 보안과 관련된 항목이다.
어떻게 보면 아주 중요한 항목이라 있다. 보안은 실제 눈으로 보이는 효과는 없지만, 안보이는 곳에서의 전쟁을 승리로 이끄는 중요한 열쇠이다
.

본인도 jar 대한 보안을 이글을 작성하면서 바로 이해한 부분이라 정확하리라는 보장은 없어, 틀린 부분이 있다면 겸손하게(^^;) 지적해주길 바란다
.

[소개]

jar에서 보안은 보통 "전자서명" 하는 것이 보면 된다. 이렇게 하는 이유는 단순하다. 해당 파일이 내가 확인한 파일임을 명시하는 것이다. 해당 서명이 틀리면 내가 확인한 파일이 아니므로 문제가 있다라고 말할 있다. 그렇기에 "전자서명"후에 해당 파일에 대한 확인 작업이 추가로 필요하게 된다.

사실 "전자서명"만으로는 부족하다. 다른 사람이 "전자서명" 해두면 것이 정당한지 확인할 길이 없다. 그래서 필요한 것이 "인증센터"이다. 그래서 "전자서명" 이를 인증할 "인증센터" 명시한다. 센터는 아무나 만들수 없는 공인센터이야함은 당연하다.

예를 들어 MS-Sql jdbc 살펴보자. "META-INF"디렉토리에 들어 있는 파일은 다음과 같다.


아래는 중에서 manifest.mf 파일 내용중 일부이다.

Manifest-Version: 1.0
Created-By: Signtool (signtool 1.3)
Comments: PLEASE DO NOT EDIT THIS FILE. YOU WILL BREAK IT.

Name: com/microsoft/sqlserver/jdbc/AppDTVImpl$SetValueOp.class
Digest-Algorithms: MD5 SHA1
MD5-Digest: KrvkkdiHwdXHNmPcE9RQBQ==
SHA1-Digest: 6opr4+DKaQZj0Fr3FVaZUEm7jo8=

Name: com/microsoft/sqlserver/jdbc/AppDTVImpl.class
Digest-Algorithms: MD5 SHA1
MD5-Digest: nDtY9xaWCEF+HnuZII0/mQ==
SHA1-Digest: DKIMpF6OfIdlemGRNzve/44H5Bc=
(
생략
)

위의 내용을 보면 인증 방식이 MD5 혹은 SHA1으로 되어 있음을 있다. 다이제스트는 jar 있는 모든 파일에 대해 일일이 지정되어 있다. 클래스 파일 외에 이미지나 문서파일도 포함된다. 그렇다고 내용을 사용자가 전부 입력하는 아니다. 자동으로 생성해서 입력해준다. 이부분이 "전자서명"이라 보면 된다.

각각 파일에 있는 "전자서명" 근거로 파일들의 다이제스트를 재계산해서 비교한다. 마지막으로 자신 manifest.mf 파일도 다이제스트를 재계산해서 서명파일에 있는 다이제스트와 비교한다. 서명파일이 확장자 "sf"가진 파일이다. 앞의 sqljdbc 경우 zigbert.sf라는 이름으로 아래와 같은 내요이 들어 있다. 처음 항목이 manifest.mf 대한 다이제스트라고 보면 된다
.

Signature-Version: 1.0
Created-By: Signtool (signtool 1.3)
Comments: PLEASE DO NOT EDIT THIS FILE. YOU WILL BREAK IT.
Digest-Algorithms: MD5 SHA1
MD5-Digest: f2XP9lsoHe04PBOvuFXL5g==
SHA1-Digest: 8RHZ0uUrxScBNJtHgI/t7stA9yA=

Name: com/microsoft/sqlserver/jdbc/AppDTVImpl$SetValueOp.class
Digest-Algorithms: MD5 SHA1
MD5-Digest: VLh2abTPHFQ9lSYvwTmrTA==
SHA1-Digest: UZ48R4Mgy46R1/HfJaTJ+Ygk1bU=
(
생략)


그리고 이외에 사람이 읽을 없는 파일 하나가 있다. signature block 파일이다. 앞의 sqljdbc에서는 zigbert.rsa파일이다여기에는,

서명자의 개인키로 생성된 jar파일에 대한 전자서명

서명자의 공개키로 포함되어 저자서명에 대한 확인 작업시 사용

확장자 명이 rsa 되어 있지만 dsa 가질 있다. dsa DSA(Digital Signature Algorithm) 사용한 방식이라는 말이다. 물론 확장자는 서명하는 종류에 따라서 달라질 것이다.

 

[서명하기]

jar파일에 서명하기 위해서는 공개키/개인키를 생성해야한다. 자신만의 서명이 있어야된다. 이때 사용하는 명령이 keytool이다. 아래 처럼 간단히 키를 생성하면 된다.
옵션 "genkey" "genkeypair"라는 옵션이다. 이는 키를 생성하겠다는 의미이다.

 

생성된 키는 파일로 저장되지 않은다. keystore이라는 데이터 자장공간에 넣어둔다. 안에 있는 목록을 보려면 아래와 같이 "list"옵션을 사용한 된다.

 
이제 jar파일에 서명을 해보자. 이때 사용하는 명령은 jarsigner이다. 이는 아직(ver 1.6.0) 한글화가 안되있다. ^^:
아래와 같이 사용법은 간단하다
.

jarsigner jar
파일명 별칭


jar
파일명은 앞에서 생성한 jar파일을 넣어주면 된다. 별칭은 keytool 의해 생성된 것으로서 결과를 보면 mykey이다. 아래는 실행 결과이다.


경로로 해당 서명이 6개월 후면 보증기간이 만료가 된다고 하네요. .;
그냥 넘어가죠. (쿨럭 ㅡㅡ
;)

이제 서명된 jar파일을 얻었습니다. 제대로 되었는지 jar파일 안을 살펴보겠습니다.

 

제대로 파일은 보이네요. 실제 파일 내용도 제대로 들어있는지 살펴볼까요.

추가가 되었네요. 이정도로만 하죠. 더하면 머리아프고 관심있으신 분은 아래 영문자료를 참고해주길 바랍니다. (패스~ .;)

[서명확인]

서명을 했으니 서명을 확인해봐야죠. 이것 역시 jarsigner 명령을 이용하며 이때 사용하는 옵션이 "verify"입니다. 사용법은 단순히 아래와 같이 하면 되죠.

jarsigner -verify jar
파일명


그럼 실제 결과를 보죠.

 


"verified"라고 "확인되었다" 하네요.
만약 서명이 되었거나, 잘못되었다면 아래와 같이 나타날 것입니다.

 

[기타]

다른 내용으로 프로그래밍시 jar파일을 액세스할 있는 API 제공해준다. 관련 패키지가 다음과 같다.

java.util.jar

java.net.JarURLConnection

java.net.URLClassLoader

 

JarURLConnection클래스를 이용해서 원격에 있는 jar파일을 불러올 있으며, JarClassLoader클래스를 이용해서 jar파일을 실행할 수도 있다.

만약 jar파일을 원격에서 수신받는다면, 원하는 클래스가 나중에 있다면 모든 jar파일이 수신되어야할 것이다. 이때 인덱스를 만듬으로서 원하는 곳을 먼저 받아서 실행할 있다
.
생성법은 아래와 같다
.

jar -i jar파일명

 

그러면 jar파일 안에 META-INF 디렉토리 안에 INDEX.LIST 생성된다. 그러면 내부에 있는 모든 패키지, 클래스가 나열이 된다. 궁금하시면 직접 만들어서 확인해보시라.

앞에서 서명된 파일에 인덱스를 만들면 INDEX.LIST 추가된다. 그러나 파일은 서명이 안되었기에 서명확인 작업을 하면 이상은 없지만 전체파일에 대해 서명이 안되었다고 경고 메시지가 보일 것이다. 필요하다면 다시 서명하면된다. ^^



참고자료:
http://java.sun.com/docs/books/tutorial/deployment/jar/manifestindex.html
http://www.ibm.com/developerworks/kr/library/j-jar/

 

 

출처 : http://ospace.tistory.com/108 그리고 약간편집

 

'Java > The Java Language' 카테고리의 다른 글

What is Enum in Java  (1) 2013.02.06
Static and Nostatic Initializer blocks  (0) 2013.02.06
Assertion  (1) 2013.02.06
Java stack trace with line numbers  (0) 2013.02.06
JVM Architecture  (0) 2013.02.06
Posted by Steven J.S Min
,