1. Java 이해하기

2021. 6. 21. 10:00WEB Dev./Java

1. Java 언어의 장단점

  • 장점
    - OS에 독립적이다 : JVM에서 동작하기 때문
    - 객체지향 언어이다
    - 자동으로 메모리를 관리해준다 : GC로 인해 별도의 메모리 관리가 필요없다
    - 오픈소스이다 : OpenJDK가 오픈소스이다.
    - 멀티스레드를 쉽게 구현할 수 있다 : 스레드 생성 및 제어와 관련된 라이브러리 API를 제공한다.
    - 동적 로딩을 지원한다 : 애플리케이션이 실행될 때 각 객체가 필요한 시점에 클래스를 동적 로딩해서 생성한다. 전체 애플리케이션을 다시 컴파일할 필요가 없으므로 유지보수가 쉽고 빠르다.

  • 단점
    - 비교적 속도가 느리다 : JVM에 의해 기계어로 번역되고 실행하는 과정을 거치므로 C/C++ 보단 속도가 느린 편이다.
    -  예외처리가 불편하다

2. Java의 데이터 타입

1. 기본 데이터 타입

기본 타입에는 byte, short, char, int, long, float, double, boolean이 있습니다.

 

2. 참조 타입

참조 타입 종류에는 class, array, interface, Enumeration이 있습니다.

  • new 키워드를 이용해 객체를 생성하여 데이터가 생성된 주소를 참조하는 타입입니다.
  • String, StringBuffer, List 등..
  • String과 배열은 다른 참조 타입과 달리 new 없이 생성이 가능하지만 참조 타입입니다.
  • 참조 타입은 데이터의 크기가 가변적/동적이기 때문에 동적으로 관리되는 Heap 영역에 저장됩니다.
  • 더이상 참조하는 변수가 없다면 Garbage Collection에 의해 파괴됩니다.

Wrapper class

프로그램에 따라 기본 타입의 데이터를 객체로 취급해야 하는 경우가 있습니다. 예를 들어 메소드의 인수로 객체 타입만 요구되면, 기본 타입의 데이터를 그대로 사용할 수 없을 것입니다. 따라서 객체로 변환하는 작업이 필요합니다.

 

Java에서 제공하는 Wrapper class

기본 타입 Wrapper class
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

Wrapper class는 각 타입에 해당하는 데이터를 인수로 전달받아, 해당 값을 가지는 객체로 만들어줍니다. 또한 Wrapper class는 java.lang 패키지에 포함되어져 제공됩니다.

 

박싱(Boxing), 언박싱(Unboxing)

Wrapper class는 산술 연산을 위해 정의된 클래스가 아니므로, 인스턴스에 저장된 값을 변경할 수 없습니다.

값을 참조하기 위해 새로운 인스턴스를 생성하고 생성된 인스턴스의 값만 참조할 뿐입니다.

Boxing : 기본 타입의 데이터 → Wrapper class 변환 

Unboxing : Wrapper class → 기본 타입의 데이터 변환

 

오토 박싱(AutoBoxing)과 오토 언박싱(AutoUnboxing)

JDK 1.5부터 박싱과 언박싱이 필요한 상황에서 java 컴파일러가 이를 자동으로 처리해줍니다. 

Integer num = new Integer(20); // Boxing
int n = num.intValue();        // UnBoxing

Integer num = 20;              // Auto Boxing
int n = num;                   // Auto UnBoxing

// Operation
Integer num1 = 10;
Integer num2 = 20;
Integer num3 = 20;

System.out.println(num1 < num2);      // true
System.out.println(num2 == num3);     // false
System.out.println(num2.equals(num3); // true

Auto Boxing을 이용하면 new 키워드를 사용하지 않고도 자동으로 인스턴스를 생성할 수 있습니다.

Wrapper class의 비교 연산 또한 오토 언박싱을 통해 가능합니다. 하지만 동등 연산은 ==을 사용하면 안 되고 equals() 메소드를 사용해야 합니다.

Wrapper class도 객체이므로 동등 연산자를 사용하면 두 인스턴스의 값이 아닌 주소값을 비교하게 됩니다. 그렇기 때문에 == 연산자를 이용하면 계속 false가 나오게 될 것입니다.


Java는 어떤 OS든지 상관없이 독립적인 특징을 가지고 있습니다. 바로 JVM(Java Virtual Machine) 때문입니다. 그렇다면 JVM의 어떤 기능 때문에 독립적으로 실행시킬 수 있는지 컴파일 과정에 대해 알아보겠습니다.


3. Java 컴파일 순서

  1. 사용자가 Java 소스코드(.java)를 작성합니다.
  2. Java 컴파일러가 Java 소스코드를 컴파일합니다. 컴파일 후 Java Byte 코드(.class) 파일이 만들어지는데 이는 컴퓨터가 읽을 수 없고 JVM이 이해할 수 있는 코드입니다. 바이트 코드의 각 명령어는 1 Byte 크기의 Opcode와 추가 피연산자로 구성되어져 있습니다.
  3. 컴파일된 Byte 코드를 JVM의 Class Loader에게 전달합니다.
  4. Class Loader는 동적 로딩(Dynamic Loading)을 통해 필요한 클래스들을 로딩 및 링크해 런타임 데이터 영역, 즉 JVM의 메모리에 올립니다.
    # Class Loader 세부 동작
    - 로드 : 클래스 파일을 가져와 JVM 메모리에 로드
    - 검증 : 자바 언어 명세(Java Language Specification) 및 JVM 명세에 명시된 대로 구성돼 있는지 검사
    - 준비 : 클래스가 필요로하는 메모리를 할당(필드, 메서드, 인터페이스 등)
    - 분석 : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
    - 초기화 : 클래스 변수들을 적절한 값으로 초기화 (static 필드)
  5. 실행 엔진은 JVM 메모리에 올라온 Byte 코드들을 명령어 단위로 하나씩 가져와 실행합니다. 이때 실행 엔진은 두 가지 방식으로 변경합니다.
    1) 인터프리터 : Byte 코드 명령어를 하나씩 읽어서 해석하고 실행합니다. 하나하나의 실행은 빠르나, 전체적인 실행 속도가 느리다는 단점을 가집니다.
    2) JIT 컴파일러 : 인터프리터의 단점을 보완하기 위해 도입된 방식으로 Byte 코드 전체를 컴파일하여 바이너리 코드로 변경하고 이후 해당 메서드를 더이상 인터프리팅 하지 않고, 바이너리 코드로 직접 실행하는 방식입니다. Byte 코드 전체가 컴파일된 바이너리 코드를 실행하는 것이기 때문에 전체적인 실행속도는 인터프리팅 방식보다 빠릅니다.

 

JVM에 대해 조금은 더 자세하게 다뤄보겠습니다.

JVM은 시스템 메모리를 관리하면서, 자바 기반 애플리케이션을 위해 이식 가능한 실행환경을 제공합니다. JVM이 갖춘 기능으로는..

  1. 자바 프로그램이 어느 기기나 운영체제 상에서도 실행될 수 있도록 하는 것
  2. 프로그램 메모리를 관리하고 최적화하는 것

두 가지로 나눌 수 있습니다.

 

JVM은 일반적으로 어떤 기기상에서 실행되고 있는 프로세스, 특히 자바 애플리케이션에 대한 리소스를 대표하고 통제하는 서버를 지칭합니다. 자바 애플리케이션을 클래스 로더를 통해 읽어들이고, 자바와 OS 사이에서 중개자 역할을 수행해 OS에 구애 받지 않고 재사용 가능하게 해줍니다.

 

JVM에서의 메모리 관리

JVM 실행에 있어서 가장 일반적인 상호작용은 Heap과 Stack의 메모리 사용을 확인하는 것입니다.

실행과정

  1. 프로그램이 실행되면, JVM은 OS로부터 이 프로그램이 필요로하는 메모리를 할당받습니다. JVM은 이 메모리를 용도에 따라 여러 영역으로 나눠 관리합니다.
  2. 자바 컴파일러(JAVAC)가 자바 소스코드를 읽고, 자바 바이트 코드(.class)로 변환시킵니다.
  3. 변경된 class 파일들을 클래스 로더를 통해 JVM 메모리 영역으로 로딩합니다.
  4. 로딩된 class 파일들은 Execution engine을 통해 해석됩니다.
  5. 해석된 바이트 코드는 메모리 영역에 배치돼 실질적인 수행이 이뤄집니다. 이러한 실행 과정 속 JVM은 필요에 따라 스레드 동기화, 가비지 컬렉션과 같은 메모리 관리 작업을 수행합니다.

Runtime Data Areas

JVM이 운영체제 위에서 실행되면서 할당받는 메모리 영역을 Runtime Data Area라고 합니다.

총 다섯가지 영역으로 나눠지며.. PC Register/ JVM Stack/ Native method stack/ Heap/ Method 영역으로 이뤄져 있습니다.

 

  • PC Register : 스레드가 어떤 명령어로 실행되어야 할지 기록 (JVM 명령의 주소를 가짐)
  • Stack : 지역변수, 매개변수, 메서드 정보, 임시 데이터 등 저장
  • Native Method Stack : 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역
  • Heap : 런타임에 동적으로 할당되는 데이터가 저장되는 영역으로 객체나 배열이 여기에 해당합니다.
    (또한 가비지 컬렉터의 대상이 되는 영역으로 JVM 성능 이슈에서 가장 많이 언급되는 공간입니다)
  • Method : JVM이 시작될 때 생성되고, JVM이 읽은 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드 및 메서드 코드, 정적 변수, 메서드의 바이트 코드 등을 저장

Reference

tech-interview-for-developer

tech-interview

'WEB Dev. > Java' 카테고리의 다른 글

3. Java - static, final  (0) 2021.06.22
2. Java - String / StringBuffer / StringBuilder  (0) 2021.06.21