'Language'에 해당되는 글 463건

  1. 2016.09.08 What does .delegate mean in groovy?
  2. 2016.09.08 ExpandoMetaClass
  3. 2016.09.05 Groovy - 문법 (Java 와 비교해서 다른 점 위주로)
  4. 2016.08.05 자바 JVM 정리(Java Virtual Machine)
  5. 2016.08.05 GC
  6. 2016.08.04 Do You Really Get Classloaders?
  7. 2016.08.04 JVM ClassLoader의 작동원리
  8. 2016.08.04 클래스로더 1, 동적인 클래스 로딩과 클래스로더 3
  9. 2016.08.04 java class loader #2
  10. 2016.08.04 java class loader #1 1

What does .delegate mean in groovy?

Language/Groovy 2016. 9. 8. 15:55


50down voteaccepted

The delegate of a closure is an object that is used to resolve references that cannot be resolved within the body of the closure itself. If your example was written like this instead:

def say = {
  def m = 'hello'
  println m
}
say.delegate = [m:2]
say()

It prints 'hello', because m can be resolved within the closure. However, when m is not defined within the closure,

def say = {
  println m
}
say.delegate = [m:2]
say()

the delegate is used to resolve the reference, and in this case the delegate is a Map that maps mto 2.

출처 - http://stackoverflow.com/questions/8120949/what-does-delegate-mean-in-groovy


'Language > Groovy' 카테고리의 다른 글

Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
ExpandoMetaClass  (0) 2016.09.08
Groovy - 문법 (Java 와 비교해서 다른 점 위주로)  (0) 2016.09.05
:

ExpandoMetaClass

Language/Groovy 2016. 9. 8. 15:54

ExpandoMetaClass - Adding methods to interfaces

It is possible to add methods onto interfaces with ExpandoMetaClass. To do this however, it MUST be enabled globally using the ExpandoMetaClass.enableGlobally() method before application start-up.

As an example this code adds a new method to all implementors of java.util.List:


List.metaClass.sizeDoubled = {-> delegate.size() * 2 }

def list = []

list << 1
list << 2

assert 4 == list.sizeDoubled()

Another example taken from Grails, this code allows access to session attributes using Groovy's subscript operator to all implementors of the HttpSession interface:


  HttpSession.metaClass.getAt = { String key ->
        delegate.getAttribute(key)
  }
  HttpSession.metaClass.putAt = { String key, Object val ->
        delegate.setAttribute(key, val)
  }  

  def session = new MockHttpSession()

  session.foo = "bar"
출처 - http://groovy.jmiguel.eu/groovy.codehaus.org/ExpandoMetaClass+-+Interfaces.html


:

Groovy - 문법 (Java 와 비교해서 다른 점 위주로)

Language/Groovy 2016. 9. 5. 11:39

생략 가능한 구문들

Import Statements

그루비는 기본적으로 다음 패키지들을 임포트하고 있기 때문에, 다음 패키지들에 대해서는 임포트문을 생략할 수 있다.

● Groovy.lang.*

● Groovy.util.*

● Java.lang.*

● Java.util.*

● Java.net.*

● Java.io.*

● Java.math.BigInteger

● Java.math.BigDecimal

Semicolons

그루비에서 세미콜론은 필수가 아니기 때문에 생략 가능하다. 다음 두 문장 모두 유효하다.

1
2
println 'Hello World';
println 'Hello World'

하지만 두 문장을 한 라인에 코딩할 경우라면 세미콜론으로 각 문장을 구분해 줘야 한다.

1
println 'Hello'; println 'world'

Parentheses

그루비에서 괄호는 필수가 아니기 때문에 생략가능하다. 다음 두 문장은 모두 유효하다

1
2
println ('Hello World')
println 'Hello World'

하지만 간단한 테스트 코드가 아니라면 괄호를 써주는 편이 읽기에 좋다.

Return Type and the return Keyword

그루비에서는 리턴 타입을 명시할 필요도 없고, 메소드 마지막 줄에서 리턴 키워드를 사용할 필요도 없다. 리턴 타입으로 def 키워드를 사용하면 그루비가 알아서 타입을 결정해 준다.

1
2
3
4
5
6
def getPi() {
    3.14
}
 
assert getPi() in BigDecimal
assert getPi() == 3.14

Getters and Setters

그루비는 더 간단한 문법을 지원하기 위해 JavaBeans 와 유사한 GroovyBeans 를 도입했다.

GroovyBeans 내의 프로퍼티들은 public 필드와 유사하므로 getter 와 setter 를 명시적으로 정의할 필요가 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
    String firstName
    String lastName
 
    def getName() {
        firstName + ' ' + lastName
    }
 
    static void main(args) {
        def person = new Person()
        person.firstName = 'Kwan Young'
        person.lastName = 'Shin'
        assert person.firstName == 'Kwan Young'
        assert person.lastName == 'Shin'
        println person.getName()
    }
}

Access Modifiers

자바에서 접근 제어자를 생략할 경우 해당 패키지내에서만 접근가능도록 설정되지만 그루비에서 생략할 경우에는 기본 접근자가 public 으로 인식되므로 모든 곳에서 접근이 가능하다.  

Checked Exceptions

자바에서는 IOException 과 같은 Checked 예외를 처리하기 위해서 try/catch 문으로 감싸줘야 했지만 그루비에서는 Checked 예외들이 RunTimeException 으로 감싸지므로 try/catch 문을 적용할 필요가 없어졌다.

추가되거나 강화된 문법들

Assertions

그루비는 강력한 assert 키워드를 제공한다.

1
2
3
4
5
6
7
8
9
x=1
assert x
assert (x ==1)
assert ['a'] // 리스트가 비어있지 않으므로 true
assert ['a':1] // 맵이 비어있지 않으므로 true
assert 'a' // 스트링이  비어있지 않으므로 true
assert 1
assert !null
assert true

Closures

재사용 가능한 코드블록인데 파라미터로 사용될 수도 있고 실행시킬 수도 있다. 클로져는 다음과 같이 정의한다.

{arg1, arg2...-> statements}

"->" 는 클로져의 파라미터 목록과 클로저의 기능을 정의하는 블락을 구분해주는 역할을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
def clos1 = {println "hello world!"}
clos1()
 
def clos2 = {arg1, arg2 -> println arg1+arg2}
clos2(3,4)
 
def method1(book) {
    def prefix = "The title of the book is"
    return {println prefix + book}
}
 
def clos3 = method1("Groovy")
clos3()

클로져는 보통 변수에 할당되고, 이 변수를 사용해서 클로져를 호출한다. 클로져를 파라미터로 받는 메소드도 작성 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Employee {
    def salary
    double calculateRaise(c) {
        return c(salary)
    }
}
 
Employee employee1 = new Employee(salary:1000)
def raise1 = {salary -> (salary * 1.5)}
assert employee1.calculateRaise(raise1) == 1500
 
Employee employee2 = new Employee(salary:500)
def raise2 = {salary -> (salary + 300)}
assert employee2.calculateRaise(raise2) == 800

Collective Data Types

Lists

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
31
32
def x = [1, 2, 3, 4]
 
def y = ['Hi', 1, true, File]
 
def a = []
a += [1,2,3]
assert a == [1,2,3]
assert a.size == 3
// 리스트 에 4와 5를 다음과 같이 추가할 수도 있다.
a << 4 << 5
assert a == [1,2,3,4,5]
a.add(6)
assert a == [1,2,3,4,5,6]
 
assert a[0] == 1
assert a.get(0) == 1
assert a.getAt(0) == 1
assert a[-1] == 6
 
a.putAt(1,1)
assert a == [1,1,3,4,5,6]
// set 메소드는 이전 값을 리턴한다.
assert a.set(1,2) == 1
assert a == [1,2,3,4,5,6]
 
a.each{println "$it"}
a.eachWithIndex{it, index -> println item : "$it", index : "$index"}
 
a -=1
assert a == [2,3,4,5,6]
a = a.minus([2,3,4])
assert a == [5,6]

Maps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def map = ['name':'Bashar', 'age':26, skills:['Java', 'Groovy'], 'author':true]
assert map.size() == 4
 
map += ['city':'Tucson']
assert map == ['name':'Bashar', 'age':26, skills:['Java', 'Groovy'], 'author':true, 'city':'Tucson']
 
map['state'] = 'AZ'
assert map == ['name':'Bashar', 'age':26, skills:['Java', 'Groovy'], 'author':true, 'city':'Tucson', 'state':'AZ']
 
assert map.city == 'Tucson'
assert map['city'] == 'Tucson'
assert map.get('city') == 'Tucson'
assert map.getAt('city') == 'Tucson'
assert map.skills[0] =='Java'
 
assert ['name':'Bashar', 'name':'Abdul'] == ['name':'Abdul']
 
map.each{ it -> println it.key + ":" + it.value}
map.eachWithIndex{it, index -> println "item $index - " + it.key + ":" + it.value}

Ranges

1
2
3
4
5
6
7
8
9
10
11
12
def range = 1..10
assert range == [1,2,3,4,5,6,7,8,9,10]
range = 'a'..'c'
assert range == ['a', 'b', 'c']
 
range = 1..<8
assert range == [1,2,3,4,5,6,7]
 
(1..5).each{println it}
 
assert[*1..4] == [1,2,3,4]
assert[1,*2..4] == [1,2,3,4]

새로운 헬퍼, 라이브러리, ,API 들

GDK

GDK 는 JDK 클래스의 기본 기능에 그루비 만의 기능을 추가한 것이다. 예를 들어 그루비는 JDK의 java.lang.Object, java.lang.Number, java.io.File 클래스등에 다음과 같은 함수를 추가했다.

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
31
32
33
34
// java.lang.Object
def a = [1,2,3]
assert a.any{it > 2} // At least one element satisfies the condition
assert a.every(it > 0) // All elements must satisfy the condition
 
assert a.collect{it * 2} == [2,4,6]
assert a.findAll{it > 2} == [3]
a.print(a)
 
//java.lang.Number
def x = 10;
assert a.abs() == 10
assert x.compareTo(3) == 1
assert x.div(2) == 5
def total = 0
x.downto(5) {
    number -> total += number} // Sums the numbers from 10 to 5 inclusive
assert total == 45
total = 0
x.upto(15) {
    number -> total += number} // Sums the numbers from 10 to 15 inclusive
assert total == 75
 
//java.io.File
def f = new File("C:\\temp\\groovy.txt") // Marks a file for creation
f.text = "Groovy rocks!" // File will be created if it doesn't exist
assert f.exists()
assert f.text == "Groovy rocks!"
f.append("Doesn't?")
assert f.text == "Groovy rocks!?Doesn't?"
f.renameTo(new File("C:\\temp\\groovyRenamed.txt"))
assert f.name == "groovy.txt"
[new File("C:\\temp\\groovy.txt"), new File("C:\\temp\\groovyRenamed.txt")]
.each{it.delete()}

Strings and GStrings

그루비는 JDK의 java.lang.String 타입과 GDK의 groovy.lang.GString 이라는 두가지 타입의 스트링을 지원한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 홑따옴표로 선언한 스트링은 GString 를 지원하지 않고
// 자바에서 스트링을 선언한 것과 같다고 보면 된다.
def text1 = 'Welcom to Groovy'
assert text1 as java.lang.String
 
// 쌍타옴표로 선언한 스트링은 GString 타입 스트링이다.
// 따라서 PlaceHolder 등을 지원한다. 여기서는 $language 가 PlaceHolder 임
def language = "Groovy"
def text2 = "Welcome to $language"
assert text2 == "Welcome to Groovy"
assert text2 as groovy.lang.GString
 
// 3개 따옴표는 각 라인의 마지막에 캐리지 리턴이 추가된다.
def text3 = '''
Welcome to Groovy
-----------------
'''
assert text3 == "\nWelcome to Groovy\n-----------------\n"

GDK 는 다음과 같이 string 관련 추가 메소드를 많이 제공한다.

1
2
3
4
5
6
7
8
9
10
11
def text = "Welcome to Groovy"
assert text.size() && text.length() == 17
assert text.substring(0, 7) == "Welcome"
assert text.contains("Welcome")
assert text.count("Welcome") == 1
text += "\nWhat a great language"
assert text.isNumber() == false
 
assert text.reverse() == "egaugnal taerg a tahW\nyvoorG ot emocleW"
assert text.findAll(){it > 'w'} == ['y']
assert text.replace('Groovy', 'Java') == 'Welcome to Java\nWhat a great language'

Regular Expressions

그루비에서 정규표현식은 슬래쉬 구문으로 정의할 수 있다.

1
2
3
4
5
def pattern = /abc/
assert pattern == 'abc'
 
pattern = /\s\d/
assert pattern == '\\s\\d'

그루비는 정규표현식과 함께 사용할 수 있는 3가지 연산자를 제공한다.

● ~ : 정규표현식을 정의할 때 사용함

● =~ : 정규표현식을 기반으로 검색을 수행함

● ==~ : 정규표현식을 기반으로 일치여부를 반환함

1
2
3
4
5
6
7
8
9
text = "Information technology revolution"
pattern = /\b\w*ion\b/
assert text =~ pattern
def matched = []
text.eachMatch(pattern) {match -> mached += match[0]}
println matched
assert matched.size() == 2
assert matched[0] == "Information"
assert matched[1] == "revolution"

기타

Optional Typing

그루비에서는 변수 선언시에 정적 또는 동적 타이핑이 가능하다. 변수 선언시에 java 처럼 변수 타입을 명시하는 방법을 정적 타이핑이라 하고 def 키워드를 사용해 선언하는 방식을 동적 타이핑이라고 한다. 동적 타이핑의 경우 런타임시에 그루비가 해당 변수의 타입을 결정한다.

Operator Overloading

그루비에서 모든 연산자는 메소드 호출로 해석된다. 예를 들어 1+1 은 1.plus(1) 이라는 식으로 해석되는 것이다. 따라서 이런 메소드를 오버로드하면 연산자도 함께 오버할 수 있다.


출처 - http://springsource.tistory.com/85

'Language > Groovy' 카테고리의 다른 글

Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
ExpandoMetaClass  (0) 2016.09.08
:

자바 JVM 정리(Java Virtual Machine)

Language/JAVA 2016. 8. 5. 14:50

자바 JVM 정리(Java Virtual Machine)

Java2015-08-09 

Java 언어는 기본적으로 JVM(Java Virtual Machine) 위에서 실행되도록 고안된 언어입니다. 이번엔 JVM의 구조를 살펴보고 하나씩 점검해 보는 포스팅입니다.

1. Java Virtual Machine

A Java virtual machine (JVM) is an abstract computing machine. There are three notions of the JVM: specification, implementation, and instance. The specification is a book that formally describes what is required of a JVM implementation. Having a single specification ensures all implementations are interoperable. A JVM implementation is a computer program that meets the requirements of the JVM specification in a compliant and preferably performant manner. An instance of the JVM is a process that executes a computer program compiled into Java bytecode.

Wikipedia - Java virtual machine

위키피디아에서 소개된 JVM에 대한 설명중 일부입니다. 간략하게 풀어보자면,

JVM이란 추상적인 컴퓨팅 머신입니다. 그리고 JVM에는 3가지 개념이 있습니다. 이것은 명세(스펙), 구현, 그리고 인스턴스입니다. 명세는 책입니다. JVM 구현에 무엇이 요구되는지 공식적으로 설명 되어 있습니다. 하나의 명세를 가지는 것은 모든 구현의 상호 운영을 보장합니다. JVM 구현은 JVM 명세의 요구에 맞춘 컴퓨터 프로그램입니다. JVM의 인스턴스는 자바 바이트코드로 컴파일된 컴퓨터 프로그램을 실행하는 프로세스입니다.

2. JVM Diagram

실제로 JVM의 구조는 어떻게 되었는지 다이어그램으로 확인해 봅시다.

                    ╋━━━━━━╋
class files ------▶┃Class Loader┃
                    ╋━━━━━━╋
                           ↕
╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋
┃JVM Memory(Runtime Data Areas)                                                              ┃
┃╋━━━━━━╋ ╋━━╋ ╋━━━━━━━━━━╋ ╋━━━━━━╋ ╋━━━━━━━━━━╋┃
┃┃Method Area ┃ ┃Heap┃ ┃JVM Language Stacks ┃ ┃PC Registers┃ ┃Native Method Stacks┃┃
┃╋━━━━━━╋ ╋━━╋ ╋━━━━━━━━━━╋ ╋━━━━━━╋ ╋━━━━━━━━━━╋┃
╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋
         ↕                            ↕
╋━━━━━━━━╋    ╋━━━━━━━━━━━━╋    ╋━━━━━━━━━━━━╋
┃Execution Engine┃ ↔ ┃Native Method Interface ┃ ↔ ┃Native Method Libraries ┃
╋━━━━━━━━╋    ╋━━━━━━━━━━━━╋    ╋━━━━━━━━━━━━╋

다이어그램상으로 위에서 아래로 내려가는 과정을 수행한다고 보면 됩니다. class 파일의 정보를 Class Loader를 사용해 JVM Memory 공간으로 옮기고 이것을 Execution Engine으로 실행하는 구조입니다.

그리고 새로운 단어들이 많이 등장합니다. 하나씩 살펴봅시다.

3. Class Loader

Class Loader(이하 "클래스 로더")는 클래스 파일 포맷(.class)을 준수하는 어떠한 것이라도 인지하고 적재할 수 있도록 구현해야 합니다. 그리고 런타임(Runtime)시에 클래스를 적재하고 바른 바이트코드로 작성되었는지 검사합니다.

또한 일반적으로 클래스 로더는 두 종류로 나뉩니다. 하나는 bootstrap class loader이고 다른 하나는 user define class loader입니다. 모든 JVM 구현은 반드시 bootstrap class loader가 있어야 합니다. 그리고 JVM 명세에서 클래스 로더가 클래스를 찾는 방법을 지정하지 않습니다.

4. JVM Memory(Runtime Data Areas)

JVM Memory는 Runtime Data Areas라고도 불립니다. 해당 공간에는 Method Area, Heap, JVM Language Stacks, PC Registers, Native Method Stacks 등으로 구성 되어 있습니다.

이 공간은 클래스 로더에 의해 데이터가 적재되는 공간입니다.

4.1. Method Area

Method Area(이하 "메소드 지역")은 Type(class, inteface) 데이터들을 가지고 있습니다. 다이어그램으로 보면 아래와 같습니다.

╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋
┃Method Area                                               ┃
┃╋━━━━━━━━━━━━━━━━━━━━━━━━━━╋  ┃
┃┃class data(Field, Method, Runtime Constant Pool...) ┃…┃
┃╋━━━━━━━━━━━━━━━━━━━━━━━━━━╋  ┃
╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋

인스턴스 생성을 위한 필요 정보(필드, 메소드, 생성자...)를 적재한 공간입니다.

또한 각 Type 데이터에는 Runtime Constant Pool이라는 것을 가집니다. 이 곳은 심볼 테이블(symbol table)과 유사한 구조로 되어 있습니다. 상수 풀에는 해당 Type의 메소드, 필드, 문자열 상수등의 레퍼런스를 가지고 있습니다. 실제 물리적 메모리 위치를 참조할 때 사용합니다. 그리고 종종 바이트 코드를 메모리에 전부 올리기엔 크기 때문에 바이트 코드를 참조할 레퍼런스만 상수 풀에 저장하고 추후에 해당 값을 참조해서 실행할 바이트 코드를 찾아 메모리에 적재하게 하는 경우도 있습니다. 또한, 상수 풀을 사용함으로 동적 로딩(dynamic loading)이 가능합니다.

4.2. Heap

╋━━━━━━━━━━━━━━━━━╋
┃Heap                              ┃
┃╋━━━╋ ╋━━━╋ ╋━━━╋  ┃
┃┃object┃ ┃object┃ ┃object┃…┃
┃╋━━━╋ ╋━━━╋ ╋━━━╋  ┃
╋━━━━━━━━━━━━━━━━━╋

Heap(이하 "힙")은 객체들이 적재되는 공간입니다. 메소드 지역에서 참조한 값을 바탕으로 새로운 객체를 생성하면 이곳에 적재됩니다.

4.3. JVM Language Stacks

JVM Language Stacks(이하 "JVM 스택")은 아래의 다이어그램을 참조하면 됩니다. 추후에 기술할 PC Registers, Native Method Stacks도 이 다이어그램을 참조합니다.

╋━━━━━━━━━━╋ ╋━━━━━━━━━━╋ ╋━━━━━━━━━━╋
┃Thread              ┃ ┃Thread              ┃ ┃Thread              ┃
╋━━━━━━━━━━╋ ╋━━━━━━━━━━╋ ╋━━━━━━━━━━╋
┃JVM Language Stacks ┃ ┃JVM Language Stacks ┃ ┃JVM Language Stacks ┃
┃PC Registers        ┃ ┃PC Registers        ┃ ┃PC Registers        ┃
┃Native Method Stacks┃ ┃Native Method Stacks┃ ┃Native Method Stacks┃
╋━━━━━━━━━━╋ ╋━━━━━━━━━━╋ ╋━━━━━━━━━━╋
           ↕                       ↕                       ↕
╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋
┃Heap                                                                  ┃
╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋

메소드 프레임(method frame)들이 JVM 스택에 하나씩 쌓이게 됩니다. 메소드 프레임은 간단하게 메소드라고 생각해도 됩니다. 스택 자료구조 형식이기 때문에 스택의 맨위에서부터 차례대로 메소드를 실행합니다. 또한 하나의 힙을 모든 스레드가 공유합니다. 하지만 각 스레드는 자신들만의 고유 JVM 스택을 가지고 있습니다.

4.4. PC Registers

PC는 Program Counter의 축약어입니다. 따라서 원래는 Program Counter Registers가 됩니다. 해당 영역은 현재 실행하고 있는 부분의 주소(adress)를 가지고 있습니다. 일반적으로 PC의 값은 현재 명령이 끝난 뒤에 값을 증가시킵니다. 그리고 해당하는 값의 명령을 실행하게 됩니다. 즉, 실행될 명령의 주소를 가지고 있습니다.

4.5. Native Method Stacks

일반적으로 JVM은 네이티브 방식을 지원합니다. 따라서 스레드에서 네이티브 방식의 메소드가 실행되는 경우 Native Method Stacks(이하 "네이티브 스택")에 쌓입니다. 일반적인 메소드를 실행하는 경우 JVM 스택에 쌓이다가 해당 메소드 내부에 네이티브 방식을 사용하는 메소드(예를 들면 C언어로 작성된 메소드)가 있다면 해당 메소드는 네이티브 스택에 쌓입니다.

5. Execution Engine

클래스 로더에 의해 JVM 메모리 공간에 적재된 바이트 코드를 Execution Engine(이하 "실행 엔진")을 이용해 실행합니다. 하지만 바이트 코드를 그대로 쓰는 것은 아니고 기계어로 변경한 뒤에 사용하게 됩니다. 해당 작업을 실행 엔진이 합니다. 그리고 바이트 코드를 기계어로 변경할 때엔 두 종류의 방식을 사용합니다. 각각 Interpreter와 JIT (Just-In-Time) compiler입니다.

Interpreter(이하 "인터프리터")는 우리가 알고 있는대로 방식대로 바이트 코드를 실행합니다. 하나의 명령어를 그때그때 해석해서 실행하게 되어 있습니다. JIT (Just-In-Time) compiler(이하 "JIT 컴파일러")는 인터프리터의 단점(성능,속도 등)을 보완하기 위해 도입되었습니다. 실행 엔진이 인터프리터를 이용해 명령어를 하나씩 실행하지만 JIT 컴파일러는 적정한 시간에 전체 바이트 코드를 네이티브 코드로 변경합니다. 이후에는 실행 엔진이 인터프리터 대신 네이티브로 컴파일된 코드를 실행합니다.

다만 JIT 컴파일러와 Java 컴파일러는 다른 것입니다. 다이어그램을 보면 이해가 빠를 것 같습니다.

               Java Compiler     JIT Compiler
                    ↓               ↓
Java Source Code ------- Bytecode ------- Native Code

6. Native Method Interface(JNI)

Native Method Interface는 줄여서 JNI라고도 합니다. JNI는 JVM에 의해 실행되는 코드 중 네이티브로 실행하는 것이 있다면 해당 네이티브 코드를 호출하거나 호출 될 수 있도록 만든 일종의 프레임워크입니다.

7. Native Method Libraries

네이티브 메소드 실행에 필요한 라이브러리들을 뜻합니다.

8. Closing Remarks

간단하게 JVM에 대해 알아봤습니다. 혹시라도 잘못된 점이 있다면 말씀 부탁드립니다. :D

Go Top(문서 처음으로)



출처 - http://jdm.kr/blog/188

'Language > JAVA' 카테고리의 다른 글

Generic 메서드  (0) 2017.10.20
자바 람다식(Lambda Expressions in Java)  (0) 2016.09.09
GC  (0) 2016.08.05
Do You Really Get Classloaders?  (0) 2016.08.04
JVM ClassLoader의 작동원리  (0) 2016.08.04
:

GC

Language/JAVA 2016. 8. 5. 10:33

1. 자바 VM은 힙을 세개의 영역으로 나누어 사용한다.
  1) New/Young 영역 : 새로 생성된 객체를 저장
  2) Old 영역 : 만들어진지 오래된 객체를 저장
  3) Permanent 영역 : JVM클래스와 메서드 객체를 저장


자바가 사용하는 메모리 구조


여기서 New 영역은 다시  
 a) Eden : 모든 새로 만들어진 객체를 저장
 b) Survivor Space 1, Survivor Space 2 : Old 영역으로 넘어가기 전 객체들이 저장되는 공간
으로 구분된다.

2. Garbage Collector
자바 언어의 중요한 특징중 하나.
전통적인 언어의 경우 메모리를 사용한 뒤에는 일일이 메모리를 수거해 주어야 했다. 
그러나, 자바 언어 에서는 GC기술을 사용하여 개발자로 하여금 메모리 관리에서 자유롭게 했다.

자바의 GC는 New/Young 영역과 Old 영역에 대해서만 GC를 수행한다.  
(Permanent 영역은 code가 올라가는 부분이기 때문에 GC가 필요없다.)

1) Minor GC
New 영역의 GC를 Minor GC라고 부른다. New 영역은 Eden과 Survivor라는 두 영역으로 구분된다.
Eden 영역은 자바 객체가 생성 되자 마자 저장이 되는 곳이다. 
이곳의 객체가 Minor GC가 발생할 때 Survivor 영역으로 이동된다.

Survivor 영역은 Survivor1 과 Survivor2로 나뉘어 지는데, Minor GC가 발생하면 Eden과 Survivor1에 살아있는 객체가 Survivor2로 이동되고, Eden 영역과 Survivor1 영역에 남아있는(죽어있는) 객체는 clear된다.
결과적으로, 현재 살아있는 객체들만 Survivor2에 남아있게 된다.
다음번 Minor GC가 발생되면 같은 원리로, Eden과 Survivor2의 살아있는 객체가 Survivor1으로 이동되고, 두 영역은 Clear 된다.
이와 같은 방법으로 반복되면서 메모리를 수거한다. 이런 방식의 GC알고리즘을 Copy & Scavenge라고 한다. 속도가 빠르며 작은 크기의 메모리를 collecting 하는데 효과적이다.
Minor GC 과정중 오래된 객체는 Old 영역으로 복사된다.  
(Kein:그런데 얼마나 지나야 '오래된' 객체인 것인지는 명확히 모르겠네요)


새로 생성된 객체가 Eden 영역에 있다가 Minor GC가 일어난다. 살아있는 객체(파란색)가 SS1으로 옮겨지고 죽은객체(빨간색)는 그대로 남겨진다. 이후 Eden과 SS2를 clear 한다.



Eden에 새로 생성된 객체와 SS1에 있는 살아있는 객체를 SS2로 옮기고, Eden과 SS1 을 clear 한다.


생성된지 오래된 객체를 Old 영역으로 이동한다.



2) Full GC
Old 영역의 GC를 Full GC라 한다. Mark & Compact 알고리즘을 이용하는데, 전체 객체들의 reference를 따라가면서 연결이 끊긴 객체를 marking 한다. 이 작업이 끝나면 사용되지 않는 객체가 모두 mark 되고, 이 객체들을 삭제한다. 
실제로는 삭제가 아니라, mark 된 객체로 생기는 부분을 unmark된, 즉 사용중인 객체로 메꾸는 방법이다.

Full GC는 속도가 매우 느리며, Full GC가 일어나는 도중에 순간적으로 java application이 멈춰버리기 때문에 Full GC가 일어나는 정도와 Full GC에 소요되는 시간은 application의 성능과 안정성에 매우 큰 영향을 미치게 된다.


Full GC 동작 순서


3. Garbage Collection이 중요한 이유
Minor GC는 보통 0.5초 이내에 끝나기 때문에 큰 문제가 되지 않는다. 하지만, Full GC의 경우 보통 수 초가 소요되고, GC동안 Application이 멈추기 때문에 문제가 될 수 있다. 5초 동안 서버가 멈춘다면, 멈춰있는 동안 사용자의 request는 쇄도하게 되고, queue에 저장되었다가 요청이 한꺼번에 들어오게되면 여러 장애를 발생할 수 있게 된다.
원할한 서비스를 위해서 GC를 어떻게 일어나게 하느냐가 시스템의 안정성과 성능에 변수로 작용하게 된다.


4. Garbage Collection 알고리즘들
1) Default Collector
위에 설명한 전통적인 GC방법으로 Minor GC에 Scavenge를, Full GC에 Mark & Compact를 사용하는 방법이다.

2) Parallel GC
JDK 1.3까지는 하나의 thread 에서만 GC가 수행되었다. JDK 1.4 부터 지원되는 parallel gc 는 minor gc를 동시에 여러개의 thread 를 이용해서 수행하는 방법으로 하나의 thread 에서 gc를 수행하는 것보다 빠른 gc를 수행한다.



하지만, parallel gc가 언제나 유익한 것은 아니다. 1 CPU에서는 오히려 parallel gc 가 느리다. multi thread에 대한 지원이나 계산등을 위해서 4CPU의 256M 정도의 메모리를 보유한 시스템에서 유용하게 사용된다.
parallel gc 는 두가지 옵션을 제공하는데, Low-Pause 방식과 Throughput 방식이다. 
solaris 기준으로 Low-pause 방식은 ?XX:+UseParNewGC 옵션을 사용한다. Old GC를 수행할 때 Application 이 멈추는 현상을 최소화 하는데 역점을 두었다. 
Throughput 방식은 ?XX:+UseParallelGC 옵션을 사용하며, Old 영역을 GC할때는 기본 옵션을 사용하며 Minor GC가 발생했을 때 최대한 빨리 수행되도록 throughput에 역점을 둔 방식이다.

3) Concurrent GC
Full GC를 하는 동안 시간이 길고, Application이 순간적으로 멈추는 현상이 발생하는 단점을 보완하기 위해서, Full GC에 의해 Application이 멈추는 현상을 최소화 하기 위한 방법이다.
Full GC에 소요되는 작업을 Application을 멈추고 하는것이 아니라, 일부는 Application을 수행하고, Application이 멈추었을때 최소한의 작업만을 GC에 할당하는 방법으로 Application이 멈추는 시간을 최소화 한다.


Application이 수행중 일 때 (붉은라인) Full GC를 위한 작업을 수행한다. Application이 멈춘 시간동안에는 일부분의 작업을 수행하기 때문에 기존 Default 방법보다 멈추는 시간이 현저하게 줄어든다.
solaris JVM 에서는 -XX:+UseConcMarkSweepGC 옵션을 사용한다.

4) Incremental GC (Train GC)
Incremental GC 또는 Train GC 라고 불리우는 방법은 JDK 1.3 부터 지원된 방법이다. 의도 자체는 Full GC 동안 Application이 멈추는 시간을 최소화 하는데 목적이 있다.
Minor GC가 일어날 때 마다 Old 영역을 조금씩 GC를 해서, Full GC가 발생하는 횟수나 시간을 줄이는 방법이다.


그림에서 보듯, 왼쪽의 Default GC Full GC가 일어난 후에나 Old 영역이 Clear된다그러나오른쪽의 Incremental GC를 보면 Minor GC가 일어난후에, Old 영역이 일부 Collect된것을 알 수 있다.
Incremental GC
를 사용하는 방법은 JVM 옵션에 ?Xinc 옵션을 사용하면 된다
.
Incremental GC
는 많은 자원을 소모하고, Minor GC를 자주일으키며Incremental GC를 사용한다고 Full GC가 없어지거나 그 횟수가 획기적으로 줄어드는 것은 아니다오히려 느려지는 경우가 많다.

5. GC 로그 수집 및 분석 방법
이제 적군에 대해 알았으니 나 자신을 파악할 차례다. 
내 Application의 gc 동태를 파악하기 위해 java 실행 옵션에 -verbose:gc 옵션을 주면 gc 로그를 출력할 수 있다. 


garbage collection 로그

로그중 GC 는 Minor GC 이고, Full GC는 Full GC를 나타낸다. 
그 뒤의 숫자는 GC수행 전 heap 메모리 사용량 이다. (New + Old + Perm 영역)
그뒤 -> 이후의 숫자는 GC 수행 후 heap 메모리 사용량을 나타낸다. 
Minor GC 가 수행된 뒤에는 Eden 과 Survivor 영역의 GC가 수행된 것이며, GC이후 heap 사용량은 Old영역의 용량과 유사하다. 괄호 안의 Total Heap Size 는 현재 jvm 이 사용하는 Heap memory의 양이다. 이 크기는 java 실행 옵션의 -Xms -Xmx 옵션으로 설정이 가능한데, 예를 들어 -Xms512 -Xmx1024로 해 놓으면 jvm는 메모리 사용량에 따라서 512~1024m 사이에서 적절하게 메모리 사용량을 늘였다 줄였다 하며 동작한다.
그 다음값은 gc에 소요된 시간이다. 

위의 로그를 보면, Minor GC가 일어날 때 마다 약 20,000Kbytes 정도의 collection이 일어난다. Minor GC는 Eden과 survivor 영역 하나를 gc 하는 것이기 때문에  New 영역을 20,000Kbyte 정도로 생각할 수 있다. 
Full GC 때를 보면 약 44,000Kbytes 에서 1,749Kbytes 로 줄어든 것을 볼 수 있다. Old 영역에 큰 데이터가 많지 않은 경우이다. 
Data를 많이 사용하는 Application의 경우 전체 Heap 이 512M 라 할 때, Full GC 후에도 480M 정도로 유지되는 경우가 있다. 이런 경우에는 실제로 Application이 메모리를 많이 사용하는 경우라고 판단할 수 있기 때문에, 전체 Heap 메모리를 늘려주면 효과적이다. 




출처 - http://drkein.tistory.com/95

:

Do You Really Get Classloaders?

Language/JAVA 2016. 8. 4. 16:00

Part I: An Overview of Java Classloaders, Delegation and Common Problems

In this part, we provide an overview of classloaders, explain how delegation works and examine how to solve common problems that Java developers encounter with classloaders on a regular basis.

Introduction: Why you should know, and fear, Classloaders

Classloaders are at the core of the Java language. Java EE containers, OSGi, various web frameworks and other tools use classloaders heavily. Yet, something goes wrong with classloading, would you know how to solve it?

Join us for a tour of the Java classloading mechanism, both from the JVM and developer point-of-view. We will look at typical problems related to classloading and how to solve them. NoClassDefFoundError, LinkageError and many others are symptoms of specific things going wrong that you can usually find and fix. For each problem, we’ll go through an example with a corresponding solution. We’ll also take a look at how and why classloaders leak and how can that be remedied.

And for dessert, we review how to reload a Java class using a dynamic classloader. To get there we’ll see how objects, classes and classloaders are tied to each other and the process required to make changes. We begin with a bird’s eye view of the problem, explain the reloading process, and then proceed to a specific example to illustrate typical problems and solutions.

Enter java.lang.ClassLoader

Let’s dive into the beautiful world of classloader mechanics.

It’s important to realize that each classloader is itself an object–an instance of a class that extends java.lang.ClassLoader. Every class is loaded by one of those instances and developers are free to subclass java.lang.ClassLoader to extend the manner in which the JVM loads classes.

There might be a little confusion: if a classloader has a class and every class is loaded by a classloader, then what comes first? We need an understanding of the mechanics of a classloader (by proxy of examining its API contract) and the JVM classloader hierarchy.

First, here is the API, with some less relevant parts omitted:

package java.lang;

public abstract class ClassLoader {

  public Class loadClass(String name);
  protected Class defineClass(byte[] b);

  public URL getResource(String name);
  public Enumeration getResources(String name);
  
  public ClassLoader getParent()
}

By far, the most important method of java.lang.ClassLoader is the loadClass method, which takes the fully qualified name of the class to be loaded and returns an object of class Class.

The defineClass method is used to materialize a class for the JVM. The byte array parameter ofdefineClass is the actual class byte code loaded from disk or any other location.

What if you no longer had to redeploy your Java code to see changes? The choice is yours. In just a few clicks you can Say Goodbye to Java Redeploys forever.

getResource and getResources return URLs to actually existing resources when given a name or a path to an expected resource. They are an important part of the classloader contract and have to handle delegation the same way as loadClass – delegating to the parent first and then trying to find the resource locally. We can even view loadClass as being roughly equivalent todefineClass(getResource(name).getBytes()).

The getParent method returns the parent classloader. We’ll have a more detailed look at what that means in the next section.

The lazy nature of Java has an effect on how do classloaders work – everything should be done at the last possible moment. A class will be loaded only when it is referenced somehow – by calling a constructor, a static method or field.

Now let’s get our hands dirty with some real code. Consider the following example: class A instantiates class B.

	public class A {
	  public void doSomething() {
  	 B b = new B();
   	 b.doSomethingElse();
	  }
	}

The statement B b = new B() is semantically equivalent to B b = A.class.getClassLoader().loadClass(“B”).newInstance()

As we see, every object in Java is associated with its class (A.class) and every class is associated with classloader (A.class.getClassLoader()) that was used to load the class.

When we instantiate a ClassLoader, we can specify a parent classloader as a constructor argument. If the parent classloader isn’t specified explicitly, the virtual machine’s system classloader will be assigned as a default parent. And with this note, let’s examine the classloader hierarchy of a JVM more closely.


출처 - http://zeroturnaround.com/rebellabs/rebel-labs-tutorial-do-you-really-get-classloaders/

'Language > JAVA' 카테고리의 다른 글

자바 JVM 정리(Java Virtual Machine)  (0) 2016.08.05
GC  (0) 2016.08.05
JVM ClassLoader의 작동원리  (0) 2016.08.04
클래스로더 1, 동적인 클래스 로딩과 클래스로더  (3) 2016.08.04
java class loader #2  (0) 2016.08.04
:

JVM ClassLoader의 작동원리

Language/JAVA 2016. 8. 4. 15:48

개발을 하거나 WAS를 기동하면 ClassNotFoundException 을 많이 만나게 됩니다.


왜 클래스를 찾을 수 없는지 원리를 알아보기 위해 정리를 해보았습니다. 


ClassLoader의 작동원리를 살펴보기 전에 먼저 알아야 할 용어를 정리하면.


Class - Java 프로그램을 컴파일하여 생성된 .class 

Class 객체 - 일반객체를 만들기 위해 JVM 메모리에 로드된 .class에 대한 객체 

일반 객체 - Class 객체로 생성한 객체 


ClassLoader는 작성한 Java Byte Code를 JVM메모리상에 올려주는 역할을 하는 것이고, 프로그래머는 Class 객체를 이용하여 일반객체를 생성할 수 있습니다. 즉 메모리 내에서 Class 객체를 매개로 하여 일반객체를 생성합니다.


ClassLoader는 기본적으로 Unload 기능을 제공하지 않습니다.. GC에 의하여 자동 Unload되며, Static으로 선언된 경우에는 GC에서 Unload하지 않기 때문에 프로그래머는 항상 Static으로 선언된 클래스를 사용할 수 있습니다.





Java ClassLoader의 구조(Delegation Model, 위임구조)


Bootstrap ClassLoader - 모든 ClassLoader의 부모 ClassLoader 

Extensions ClassLoader - Bootstrap ClassLoader를 상속하며, lib/ext 디렉토리에 있는 클래스를 로드 

System-Classpath ClassLoader - Extensions ClassLoader를 상속하며, CLASSPATH에 잡혀 있는 모든 클래스, java.class.path 시스템 프라퍼티, -cp 혹은 -classpath에 잡혀 있는 모든 클래스를 로드 


ps.구조도는 첨부하였습니다.



클래스 로더의 delegation model에서 클래스를 찾을 경우 

Cache -> Parent -> Self 순서로 찾습니다. 


클래스 생성을 요청받으면 먼저 Cache를 검색하여 이전에 요청이 있었는지 파악하며, 없을경우 상위 클래스 로더에서 찾습니다. 

최상위 클래스로더인 Bootstrap ClassLoader 에도 없을 경우 작업한 소스의 경로에서 클래스를 찾아서 생성합니다.



출처 - http://open.egovframe.org/nforges/information/share/4082/.do


'Language > JAVA' 카테고리의 다른 글

GC  (0) 2016.08.05
Do You Really Get Classloaders?  (0) 2016.08.04
클래스로더 1, 동적인 클래스 로딩과 클래스로더  (3) 2016.08.04
java class loader #2  (0) 2016.08.04
java class loader #1  (1) 2016.08.04
:

클래스로더 1, 동적인 클래스 로딩과 클래스로더

Language/JAVA 2016. 8. 4. 15:35

동적인 클래스 로딩

자바는 동적으로 클래스를 읽어온다. 즉, 런타임에 모든 코드가 JVM에 링크된다. 모든 클래스는 그 클래스가 참조되는 순간에 동적으로 JVM에 링크되며, 메모리에 로딩된다. 자바의 런타임 라이브러리([JDK 설치 디렉토리]/jre/lib/rt.jar) 역시 예외가 아니다. 이러한 동적인 클래스 로딩은 자바의 클래스로더 시스템을 통해서 이루어지며, 자바가 기본적으로 제공하는 클래스로더는 java.lang.ClassLoader를 통해서 표현된다. JVM이 시작되면, 부트스트랩(bootstrap) 클래스로더를 생성하고, 그 다음에 가장 첫번째 클래스인 Object를 시스템에 읽어온다.

런타임에 동적으로 클래스를 로딩하다는 것은 JVM이 클래스에 대한 정보를 갖고 있지 않다는 것을 의미한다. 즉, JVM은 클래스의 메소드, 필드, 상속관계 등에 대한 정보를 알지 못한다. 따라서, 클래스로더는 클래스를 로딩할 때 필요한 정보를 구하고, 그 클래스가 올바른지를 검사할 수 있어야 한다. 만약 이것을 할 수 없다면, JVM은 .class 파일의 버전이 일치하지 않을 수 있으며, 또한 타입 검사를 하는 것이 불가능할 것이다. JVM은 내부적으로 클래스를 분석할 수 있는 기능을 갖고 있으며, JDK 1.1부터는 개발자들이 리플렉션(Reflection)을 통해서 이러한 클래스의 분석을 할 수 있도록 하고 있다.

로드타임 동적 로딩(load-time dynamic loading)과 런타임 동적 로딩(run-time dynamic loading)

클래스를 로딩하는 방식에는 로드타임 동적 로딩(load-time dynamic loading)과 런타임 동적 로딩(run-time dynamic loading)이 있다. 먼저 로드타임 동적 로딩에 대해서 알아보기 위해 다음과 코드를 살펴보자.

  public class HelloWorld {
     public static void main(String[] args) {
        System.out.println("안녕하세요!");
     }
  }


HelloWorld 클래스를 실행하였다고 가정해보자. 아마도, 명령행에서 다음과 같이 입력할 것이다.

  $ java HelloWorld


이 경우, JVM이 시작되고, 앞에서 말했듯이 부트스트랩 클래스로더가 생성된 후에, 모든 클래스가 상속받고 있는 Object 클래스를 읽어온다. 그 이후에, 클래스로더는 명령행에서 지정한 HelloWorld 클래스를 로딩하기 위해, HelloWorld.class 파일을 읽는다. HelloWorld 클래스를 로딩하는 과정에서 필요한 클래스가 존재한다. 바로 java.lang.String과 java.lang.System이다. 이 두 클래스는 HelloWorld 클래스를 읽어오는 과정에서, 즉 로드타임에 로딩된다. 이 처럼, 하나의 클래스를 로딩하는 과정에서 동적으로 클래스를 로딩하는 것을 로드타임 동적 로딩이라고 한다.

이제, 런타임 동적 로딩에 대해서 알아보자. 우선, 다음의 코드를 보자.

  public class HelloWorld1 implements Runnable {
     public void run() {
        System.out.println("안녕하세요, 1");
     }
  }
  public class HelloWorld2 implements Runnable {
     public void run() {
        System.out.println("안녕하세요, 2");
     }
  }


이 두 클래스를 Runnable 인터페이스를 구현한 간단한 클래스이다. 이제 실제로 런타임 동적 로딩이 일어나는 클래스를 만들어보자.

  public class RuntimeLoading {
     public static void main(String[] args) {
        try {
           if (args.length < 1) {
              System.out.println("사용법: java RuntimeLoading [클래스 이름]");
              System.exit(1);
           }
           Class klass = Class.forName(args[0]);
           Object obj = klass.newInstance();
           Runnable r = (Runnable) obj;
           r.run();
        } catch(Exception ex) {
           ex.printStackTrace();
        }
     }
  }


위 코드에서, Class.forName(className)은 파리미터로 받은 className에 해당하는 클래스를 로딩한 후에, 그 클래스에 해당하는 Class 인스턴스(로딩한 클래스의 인스턴스가 아니다!)를 리턴한다. Class 클래스의 newInstance() 메소드는 Class가 나타내는 클래스의 인스턴스를 생성한다. 예를 들어, 다음과 같이 한다면 java.lang.String 클래스의 객체가 생성된다.

  Class klass = Class.forName("java.lang.String");
  Object obj = klass.newInstance();


따라서, Class.forName() 메소드가 실행되기 전까지는 RuntimeLoading 클래스에서 어떤 클래스를 참조하는 지 알수 없다. 다시 말해서, RuntimeLoading 클래스를 로딩할 때는 어떤 클래스도 읽어오지 않고, RuntimeLoading 클래스의 main() 메소드가 실행되고 Class.forName(args[0])를 호출하는 순간에 비로서 args[0]에 해당하는 클래스를 읽어온다. 이처럼 클래스를 로딩할 때가 아닌 코드를 실행하는 순간에 클래스를 로딩하는 것을 런타임 동적 로딩이라고 한다.

다음은 RuntimeLoading 클래스를 명령행에서 실행한 결과를 보여주고 있다.

  $ java RuntimeLoading HelloWorld1
  안녕하세요, 1


Class.newInstance() 메소드와 관련해서 한 가지 알아둘 점은 해당하는 클래스의 기본생성자(즉, 파라미터가 없는)를 호출한다는 점이다. 자바는 실제로 기본생성자가 코드에 포함되어 있지 않더라도 코드를 컴파일할 때 자동적으로 기본생성자를 생성해준다. 이러한 기본생성자는 단순히 다음과 같이 구성되어 있을 것이다.

  public ClassName() {
     super();
  }


ClassLoader

자바는 클래스로더를 사용하고, 클래스를 어떻게 언제 JVM으로 로딩하고, 언로딩하는지에 대한 특정한 규칙을 갖고 있다. 이러한 규칙을 이해해야, 클래스로더를 좀 더 유용하게 사용할 수 있으며 개발자가 직접 자신만의 커스텀 클래스로더를 작성할 수 있게 된다.

클래스로더의 사용

이 글을 읽는 사람들은 거의 대부분은 클래스로더를 프로그래밍에서 직접적으로 사용해본 경험이 없을 것이다. 클래스로더를 사용하는 것은 어렵지 않으며, 보통의 자바 클래스를 사용하는 것과 완전히 동일하다. 다시 말해서, 클래스로더에 해당하는 클래스의 객체를 생성하고, 그 객체의 특정 메소드를 호출하기만 하면 된다. 간단하지 않은가? 다음의 코드를 보자.

  ClassLoader cl = . . . // ClassLoader의 객체를 생성한다.
  Class klass = null;
  try {
     klass = cl.loadClass("java.util.Date");
  } catch(ClassNotFoundException ex) {
     // 클래스를 발견할 수 없을 경우에 발생한다.
     ex.printStackTrace();
  }


일단 클래스로더를 통해서 필요한 클래스를 로딩하면, 앞의 예제와 마찬가지로 Class 클래스의 newInstance() 메소드를 사용하여 해당하는 클래스의 인스턴스를 생성할 수 있게 된다. 형태는 다음과 같다.

  try {
     Object obj = klass.newInstance();
  } catch(InstantiationException ex) {
     ....
  } catch(IllegalAccessException ex) {
     ....
  } catch(SecurityException ex) {
     ....
  } catch(ExceptionIninitializerError error) {
     ...
  }


위 코드를 보면, Class.newInstance()를 호출할 때 몇개의 예외와 에러가 발생하는 것을 알 수 있다. 이것들에 대한 내용은 Java API를 참고하기 바란다.

자바 2의 클래스로더

자바 2 플랫폼에서 클래스로더의 인터페이스와 세만틱(semantic)은 개발자들이 자바 클래스로딩 메커니즘을 빠르고 쉽게 확장할 수 있도록 하기 위해 몇몇 부분을 재정의되었다. 그 결과로, 1.1이나 1.0에 맞게 작성된 (커스텀 클래스로더를 포함한) 클래스로더는 자바 2 플랫폼에서는 제기능을 하지 못할 수도 있으며, 클래스로더 사용하기 위해 작성했던 코드를 재작성하는 것이 그렇게 간단하지만은 않다.

자바 1.x와 자바 2에서 클래스로더에 있어서 가장 큰 차이점은 자바 2의 클래스로더는 부모 클래스로더(상위 클래스가 아니다!)를 갖고 있다는 점이다. 자바 1.x의 클래스로더와는 달리, 자바 2의 클래스로더는 부모 클래스로더가 먼저 클래스를 로딩하도록 한다. 이를 클래스로더 딜리게이션 모델(ClassLoader Delegation Model)이라고 하며, 이것이 바로 이전 버전의 클래스로더와 가장 큰 차이점이다.

자바 2의 클래스로더 딜리게이션 모델에 대해 구체적으로 알아보기 위해 로컬파일시스템과 네트워크로부터 클래스를 읽어와야 할 필요가 있다고 가정해보자. 이 경우, 쉽게 로컬파일시스템의 jar 파일로부터 클래스를 읽어오는 클래스로더와 네트워크로부터 클래스를 읽어오는 클래스로더가 필요하다는 것을 생각할 수 있다. 이 두 클래스로더를 각각 JarFileClassLoader와 NetworkClassLoader라고 하자.

JDK 1.1에서, 커스텀 클래스로더를 만들기 위해서는 ClassLoader 클래스를 상속받은 후에 loadClass() 메소드를 오버라이딩하고, loadClass() 메소드에서 바이트코드를 읽어온 후, defineClass() 메소드를 호출하면 된다. 여기서 defineClass() 메소드는 읽어온 바이트코드로부터 실제 Class 인스턴스를 생성해서 리턴한다. 예를 들어, JarFileClassLoader는 다음과 같은 형태를 지닐 것이다.

  public class JarFileClassLoader extends ClassLoader {
     ...
     private byte[] loadClassFromJarFile(String className) {
        // 지정한 jar 파일로부터 className에 해당하는 클래스의
        // 바이트코드를 byte[] 배열로 읽어온다.
        ....
        return byteArr;
     }
     
     public synchronized class loadClass(String className, boolean resolveIt)
        throws ClassNotFoundException {
        
        Class klass = null;
        
        // 클래스를 로드할 때, 캐시를 사용할 수 있다.
        klass = (Class) cache.get(className);
        
        if (klass != null) return klass;
        
        // 캐시에 없을 경우, 시스템 클래스로더로부터
        // 지정한 클래스가 있는 지 알아본다.
        try {
           klass = super.findSystemClass(className);
           return klass;
        } catch(ClassNotFoundException ex) {
           // do nothing
        }
        
        // Jar 파일로부터 className이 나타내는 클래스를 읽어온다.
        byte[] byteArray = loadClassFromJarFile(className);
        klass = defineClass(byteArray, 0, byteArray.length);
        if (resolve)
           resolveClass(klass);
        cache.put(className, klass); // 캐시에 추가
        return klass;
     }
  }


위의 개략적인 코드를 보면, 시스템 클래스로더에게 이름이 className인 클래스가 존재하는 지 요청한다. (여기서 시스템 클래스로더 또는 primordial 시스템 클래스로더는 부트스트랩 클래스로더이다). 그런 후에, 시스템 클래스로더로부터 클래스를 읽어올 수 없는 경우 Jar 파일로부터 읽어온다. 이 때, className은 완전한 클래스 이름(qualified class name; 즉, 패키지이름을 포함한)이다. NetworkClassLoader 클래스 역시 이 클래스와 비슷한 형태로 이루어져 있을 것이다. 이 때, 시스템 클래스로더와 그 외의 다른 클래스로더와의 관계는 다음 그림과 같다.



위 그림을 보면, 각각의 클래스로더는 오직 시스템 클래스로더와 관계를 맺고 있다. 다시 말해서, JarFileClassLoader는 NetworkClassLoader나 AppletClassLoader와는 관계를 맺고 있지 않다. 이제, A라는 클래스가 내부적으로 B라는 클래스를 사용한다고 가정해보자. 이 때, 만약 A 클래스는 네트워크를 통해서 읽어오고, B라는 클래스는 Jar 파일을 통해서 읽어와야 한다면? 이 경우에 어떻게 해야 하는가? 쉽사리 해결책이 떠오르지 않을 것이다. 이러한 문제는 JarFileClassLoader와 NetworkClassLoader 간에 유기적인 결합을 할 수 없기 때문에 발생한다.

자바 2에서는 이러한 문제를 클래스로더 딜리게이션 모델을 통해서 해결하고 있다. 즉, 특정 클래스로더 클래스를 읽어온 클래스로더(이를 부모 클래스로더라고 한다)에게 클래스 로딩을 요청하는 것이다. 다음의 그림을 보자.


이 그림은 자바 2에서 클래스로더간의 관계를 보여주고 있다. 이 경우, NetworkClassLoader 클래스는 JarFileClassLoader가 로딩하고, JarFileClassLoader 클래스는 AppClassLoader가 로딩하였음을 보여준다. 즉, JarFileClassLoader는 NetworkClassLoader의 부모 클래스로더가 되고, AppClassLoader는 JarFileClassLoader의 부모 클래스로더가 되는 것이다.

이 경우, 앞에서 발생했던 문제가 모두 해결된다. A 클래스가 필요하면, 가장 먼저 NetworkClassLoader에 클래스로딩을 요청한다. 그럼, NetworkClassLoader는 네트워크로부터 A 클래스를 로딩할 수 있으므로, A 클래스를 로딩한다. 그런 후, A 클래스는 B 클래스를 필요로 한다. B 클래스를 로딩하기 위해 NetworkClassLoader는 JarFileClassLoader에 클래스 로딩을 위임(delegation)한다. JarFileClassLoader는 Jar 파일로부터 B 클래스를 읽어온 후 NetworkClassLoader에게 리턴할 것이며, 따라서 NetworkClassLoader는 Jar 파일에 있는 B 클래스를 사용할 수 있게 된다. 앞의 JDK 1.1에서의 클래스로더 사이의 관계에 비해 훨씬 발전적인 구조라는 것을 알 수 있다.

앞에서 말했듯이, 자바 2에서는 몇몇 클래스로더 메커니즘을 재정의하였다. 이 때문에, JDK 1.1에서의 클래스로더에 관한 몇몇개의 규칙이 깨졌다. 먼저, loadClass() 메소드를 더 이상 오버라이딩(overriding) 하지 않고, 대신 findClass()를 오버라이딩한다. loadClass() 메소드는 public에서 protected로 변경되었으며, 실제 JDK1.3의 ClassLoader 클래스의 소크 코드를 보면 다음과 같이 정의되어 있다.

  // src/java/lang/ClassLoader.java
  public abstract class ClassLoader {
      /*
       * The parent class loader for delegation.
       */
      private ClassLoader parent;
      
      protected synchronized Class loadClass(String name, boolean resolve)
      throws ClassNotFoundException
      {
          // First, check if the class has already been loaded
          Class c = findLoadedClass(name);
          if (c == null) {
              try {
                  if (parent != null) {
                      c = parent.loadClass(name, false);
                  } else {
                      c = findBootstrapClass0(name);
                  }
              } catch (ClassNotFoundException e) {
                  // If still not found, then call findClass in order
                  // to find the class.
                  c = findClass(name);
              }
          }
          if (resolve) {
              resolveClass(c);
          }
          return c;
      }
      ....
  }


위 코드를 보면 부모 클래스로더로부터 먼저 클래스 로딩을 요청하고, 그것이 실패할 경우(즉, catch 블럭)에 비로소 직접 클래스를 로딩한다. 여기서 그렇다면 부모 클래스는 어떻게 결정되는 지 살펴보자. 먼저 JDK 1.3의 ClassLoader 클래스는 다음과 같은 두 개의 생성자를 갖고 있다.

  protected ClassLoader(ClassLoader parent) {
      SecurityManager security = System.getSecurityManager();
      if (security != null) {
          security.checkCreateClassLoader();
      }
      this.parent = parent;
      initialized = true;
  }
  protected ClassLoader() {
      SecurityManager security = System.getSecurityManager();
      if (security != null) {
          security.checkCreateClassLoader();
      }
      this.parent = getSystemClassLoader();
      initialized = true;
  }


이 두 코드를 살펴보면, 부모 클래스로더를 지정하지 않을 경우, 시스템 클래스로더를 부모 클래스로더로 지정하는 것을 알 수 있다. 따라서 커스텀 클래스로더에서 부모 클래스로더를 지정하기 위해서는 다음과 같이 하면 된다.

  public class JarFileClassLoader extends ClassLoader {
     public JarFileClassLoader () {
        super(JarFileClassLoader.class.getClassLoader());
        // 다른 초기화 관련 사항
     }
     ....
     public Class findClass(String name) {
        // 지정한 클래스를 찾는다.
     }
  }


모든 클래스는 그 클래스에 해당하는 Class 인스턴스를 갖고 있다. 그 Class 인스턴스의 getClassLoader() 메소드를 통해서 그 클래스를 로딩한 클래스로더를 구할 수 있다. 즉, 위 코드는 JarFileClassLoader 클래스를 로딩한 클래스로더를 JarFileClassLoader 클래스로더의 부모 클래스로더로 지정하는 것이다. (실제로 커스텀 클래스로더를 구현하는 것에 대한 내용은 이 Article의 시리중에서 3번째에 알아보기로 한다).

JVM에서 부모 클래스로더를 갖지 않은 유일한 클래스로더는 부트스트랩 클래스로더이다. 부트스트랩 클래스로더는 자바 런타임 라이브러리에 있는 클래스를 로딩하는 역할을 맡고 있으며, 항상 클래스로더 체인의 가장 첫번째에 해당한다. 기본적으로 자바 런타임 라이브러리에 있는 모든 클래스는 JRE/lib 디렉토리에 있는 rt.jar 파일에 포함되어 있다.

결론

이번 Article에서는 자바에서 클래스 로딩이 동적으로 이루어지면, 클래스 로딩 방식에서는 로드타임 로딩과 런타임 로딩의 두 가지 방식이 있다는 것을 배웠다. 그리고 자바 2에서의 클래스로딩이 클래스로더 딜리게이션 모델(Classloader Delegation Model)을 통해서 이루어진다는 점과 이 모델에 자바 1.x에서의 클래스로딩 메커니즘과 어떻게 다르며, 어떤 장점이 있는 지 알아보았다. 다음 Article에서는 자바 2에서 기본적으로 제공하는 클래스로더에 대해서 알아보기로 한다.

'Language > JAVA' 카테고리의 다른 글

Do You Really Get Classloaders?  (0) 2016.08.04
JVM ClassLoader의 작동원리  (0) 2016.08.04
java class loader #2  (0) 2016.08.04
java class loader #1  (1) 2016.08.04
[Java] ClassLoader API  (0) 2016.08.04
:

java class loader #2

Language/JAVA 2016. 8. 4. 15:07

ClassLoader 2

 

  // src/java/lang/ClassLoader.java
  public abstract class ClassLoader {
      /*
       * The parent class loader for delegation.
       */
      private ClassLoader parent;
      
      protected synchronized Class loadClass(String name, boolean resolve)
      throws ClassNotFoundException
      {
          // First, check if the class has already been loaded
          Class c = findLoadedClass(name);
          if (c == null) {
              try {
                  if (parent != null) {
                      c = parent.loadClass(name, false);
                  } else {
                      c = findBootstrapClass0(name);
                  }
              } catch (ClassNotFoundException e) {
                  // If still not found, then call findClass in order
                  // to find the class.
                  c = findClass(name);
              }
          }
          if (resolve) {
              resolveClass(c);
          }
          return c;
      }
      ....
  }

코드 4.


코드 4 JDK1.3 ClassLoader 클래스로서 부모 클래스로더로부터 먼저 클래스 로딩을 요청하고그것이 실패할 경우에 직접 클래스를 로딩합니다여기서 부모 클래스가 어떻게 결정되는 지를 보면 JDK 1.3 ClassLoader 클래스는 다음과 같은  개의 생성자를 갖고 있습니다.

  protected ClassLoader(ClassLoader parent) {
      SecurityManager security = System.getSecurityManager();
      if (security != null) {
          security.checkCreateClassLoader();
      }
      this.parent = parent;
      initialized = true;
  }
  protected ClassLoader() {
      SecurityManager security = System.getSecurityManager();
      if (security != null) {
          security.checkCreateClassLoader();
      }
      this.parent = getSystemClassLoader();
      initialized = true;
  }

코드 5.

코드 5 보면부모 클래스로더를 지정하지 않을 경우에는 시스템 클래스로더를 부모 클래스로더로 지정하고 있습니다커스텀 클래스로더에서 부모 클래스로더를 지정하기 위해서는 코드 6 같이 하면 됩니다.

  public class JarFileClassLoader extends ClassLoader {
     public JarFileClassLoader () {
        super(JarFileClassLoader.class.getClassLoader());
        // 
다른 초기화 관련 사항
     }
     ....
     public Class findClass(String name) {
        // 
지정한 클래스를 찾는다.
     }
  }

코드 6.


모든 클래스는  클래스에 해당하는 Class 인스턴스를 가지고 있습니다 Class 인스턴스의 getClassLoader() 메소드를 통해서  클래스를 로딩한 클래스로더를 구할  있습니다코드 6에서는 JarFileClassLoader 클래스를 로딩한 클래스로더를 JarFileClassLoader 클래스로더의 부모 클래스로더로 지정하는 것입니다. 
JVM
에서 부모 클래스로더를 갖지 않은 유일한 클래스로더는 부트스트랩 클래스로더로서 부트스트랩 클래스로더는 자바 런타임 라이브러리에 있는 클래스를 로딩하는 역할을 맡고 있으며항상 클래스로더 체인의   번째에 해당합니다. Java에서는 같은 클래스일지라도다른 클래스로더에 의해서 로딩되었다면 다른 클래스로 구분이 됩니다.

 

Example


  public class ToolsJarLoader extends URLClassLoader {

private static ToolsJarLoader mInstance;

public static ToolsJarLoader getInstance() {

if(mInstance == null) {

mInstance = new                ToolsJarLoader(((URLClassLoader)ClassLoader.getSystemClassLoader()).getURLs());

}

return mInstance;

}


    public ToolsJarLoader(URL[] urls) {

        super(urls);

    }

    

    @Override

    public void addURL(URL url) {

        super.addURL(url);

    }

    

    public boolean isLoaded(URL url) {

     URL[] urls = getURLs();

     for(URL temp : urls) {

     if(temp.equals(url)) {

     return true;

     }

     }

     return false;

    }

}

코드 7.

코드 7 원하는 클래스  jar 런타임에 동적으로 로드하기 위한 클래스 로더이다. addURL 통해 jar  클래스를 로딩한다.


URL jarUrl = new File(toolsJarPath).toURI().toURL();

if(!ToolsJarLoader.getInstance().isLoaded(jarUrl)) {

ToolsJarLoader.getInstance().addURL(jarUrl);

}

Class cls = ToolsJarLoader.getInstance().loadClass("package.ClassName");

Object obj = cls.getConstructor(String.class).newInstance(arg1);

Method method = obj.getClass().getMethod("methodName");

method.invoke(obj);

코드 8.

코드 8 특정 위치에 있는 jar  클래스를 코드 7 있는 클래스로더를 이용하여 런타임에 동적으로 로딩하도록 한다로딩  해당 클래스를 만든다.

 

 

References

 

[1] http://javacan.tistory.com/3

[2] http://blog.naver.com/PostView.nhn?blogId=choigohot&logNo=40192701035

[3] http://javacan.tistory.com/entry/2

:

java class loader #1

Language/JAVA 2016. 8. 4. 15:06

Java 동적으로 클래스를 로딩하기 때문에 런타임에 모든 코드가 JVM 링크됩니다모든 클래스는 해당 클래스가 참조되는 순간에 동적으로 JVM 링크되며메모리에 로딩됩니다자바의 런타임 라이브러리([jdk path]/jre/lib/rt.jar) 역시 예외가 아닙니다이러한 동적인클래스 로딩은 자바의 클래스로더 시스템을 통해서 이루어지며자바가 기본적으로 제공하는 클래스로더는 java.lang.ClassLoader 통해서 표현됩니다. JVM 시작되면부트스트랩(bootstrap) 클래스로더를 생성하고 다음에 가장 첫번째 클래스인 Object 시스템에 로딩합니다.
런타임에 동적으로 클래스를 로딩한다는 것은 JVM 클래스에 대한 정보를 갖고 있지 않다는 것을 의미함으로서 JVM 클래스에 대한정보를 알지 못합니다따라서클래스로더는 클래스를 로딩할  필요한 정보를 구하고 클래스가 올바른지를 검사해야 합니다.


Load-time dynamic loading and Run-time dynamic loading

클래스를 로딩하는 방식에는 로드타임 동적 로딩(load-time dynamic loading) 런타임 동적 로딩(run-time dynamic loading) 있습니다. 

  public class HelloWorld {
     public static void main(String[] args) {
        System.out.println("
안녕하세요!");
     }
  }

코드 1.


로드타임 동적 로딩은 하나의 클래스를 로딩하는 과정에서 동적으로 클래스를 로딩하는 것을 말합니다코드 1 HelloWorld 클래스를 실행하면, JVM 시작되고앞에서 말했듯이 부트스트랩 클래스로더가 생성된 후에모든 클래스가 상속받고 있는 Object 클래스를 읽어옵니다 이후에클래스로더는 명령행에서 지정한 HelloWorld 클래스를 로딩하기 위해 HelloWorld.class 파일을 읽습니다그리고HelloWorld 클래스를 로딩하는 과정에서 필요한 클래스 java.lang.String java.lang.System 읽어옵니다  클래스는 HelloWorld 클래스를 읽어오는 로드타임에 동적으로 로딩됩니다.

 

class HelloWorld1 implements Runnable {
     public void run() {
        System.out.println("
안녕하세요, 1");
     }
  }
  class HelloWorld2 implements Runnable {
     public void run() {
        System.out.println("
안녕하세요, 2");
     }
  }

  public class RuntimeLoading {
     public static void main(String[] args) {
        try {
           if (args.length < 1) {
              System.out.println("
사용법: java RuntimeLoading [클래스 이름]");
              System.exit(1);
           }
           Class klass = Class.forName(args[0]);
           Object obj = klass.newInstance();
           Runnable r = (Runnable) obj;
           r.run();
        } catch(Exception ex) {
           ex.printStackTrace();
        }
     }
  }

코드 2.

 

런타임 동적 로딩은 클래스를 로딩할 때가 아닌 코드를 실행하는 순간에 클래스를 로딩하는 것을 말합니다.


코드 2에서 Class.forName(className) 파리미터로 받은 className 해당하는 클래스를 로딩한 후에  클래스에 해당하는 로딩한 클래스의 인스턴스가 아닌 Class 인스턴스를 리턴합니다그리고 Class 클래스의 newInstance() 메소드는 Class 나타내는 클래스의 인스턴스를 생성합니다예를 들어코드 3 java.lang.String 클래스의 객체를 생성합니다.

  Class klass = Class.forName("java.lang.String");
  Object obj = klass.newInstance();

코드 3.


따라서, Class.forName() 메소드를 실행하는 클래스에서는 Class.forName() 실행되기 전까지는 어떤 클래스를 참조하는    없습니다.


ClassLoader

클래스로더를 사용하기 위해서는 보통의 자바 클래스처럼 해당하는 클래스의 객체를 생성하고 객체의 특정 메소드를 호출하면 됩니다또한상속을 받아 클래스로드를 커스터마이징  수도 있습니다.

자바의 클래스로더는 클래스로더 딜리게이션 모델(ClassLoader Delegation Model)으로서 부모 클래스로더가 먼저 클래스를 로딩하도록합니다특정 클래스로더 클래스를 읽어온 클래스로더(부모 클래스로더)에게 클래스 로딩을 요청하는 것입니다


그림 1. ClassLoader Delegation Model[1]


그림 1 클래스로더간의 관계를 보여주고 있습니다 경우, NetworkClassLoader(가정) JarFileClassLoader(가정) 로딩하고, JarFileClassLoader 클래스는 AppClassLoader 로딩하였음을 보여줍니다, JarFileClassLoader NetworkClassLoader 부모 클래스로더가 되고, AppClassLoader JarFileClassLoader 부모 클래스로더가 되는 것입니다.

<자바에서 기본으로 제공하는 부트스트랩클래스로더 등에 관한 설명은 [2-3] 보면   있습니다.>


References

 

[1] http://javacan.tistory.com/3

[2] http://blog.naver.com/PostView.nhn?blogId=choigohot&logNo=40192701035

[3] http://javacan.tistory.com/entry/2

 

: