이 글에서는 Java의 등장배경을 살펴보고, 자바 코드의 실행과정과 JVM 동작원리 및 개념을 살펴보려고 합니다.

 

JAVA 등장배경

JAVA는 가전제품 내에 탑재해 동작하는 프로그램을 위해 개발한 언어라고 합니다. 개발 당시에는 UNIX 기반의 배경을 가지고 있었기 때문에 C/C++ 특성상 여러 하드웨어를 커버하기에는 같은 기능의 소스를 각 하드웨어에 맞게 작성해야하는 번거로움이 있어 JAVA를 개발하게 되었다고 합니다.

이러한 배경에서 탄생한 언어인 JAVA는 어느 하드웨어(CPU)던, 어느 운영체제(OS)이던 상관없이 컴파일된 코드(바이트코드)가 플랫폼에 독립적인 특징을 갖습니다. 즉, 어느 플랫폼이던 작성한 소스를 변경할 필요 없이 다 실행시킬 수 있다는 장점을 갖게됩니다.

이러한 특징은 JVM(Java VIrtual Machine)이 있기 때문에 가능합니다. 그렇다면 JVM(Java Virtual Machine)의 어떠한 기능 때문에 OS에 독립적으로 실행시킬 수 있는지 자바 코드의 실행과정을 통해 알아보겠습니다. 

 

JAVA 코드의 실행과정

JVM 자바 가상 머신을 단순하게 말하면 컴파일된 코드(바이트코드)를 실행시켜주는 가상의 컴퓨터라고 생각하면 이해하기 쉽습니다. JVM 을 파악하기 전에 소스코드가 JVM에 전달되어 실행되는 과정을 먼저 정리해보겠습니다.

 

[컴파일 타임 환경]

1. 자바 클래스 파일 (.java) 을 자바 컴파일러를 통해 자바 바이트 코드 (.class)로 컴파일합니다. 

 

[런타임 환경]

2. 컴파일된 바이트코드를 JVM의 클래스로더(Class Loader)에게 전달합니다. 

3. 클래스 로더는 동적로딩(Dynamic Loading)을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역(Rumtime Data area), 즉 JVM의 메모리에 올립니다.

* 클래스 로더 세부 동작
1. 로드: 클래스 파일을 가져와서 JVM의 메모리에 로드
2. 검증: 자바 언어 명세(Java Language Specification) 및 JVM 명세에 명시된 대로 구성되어 있는지 검사
3. 준비: 클래스가 필요로 하는 메모리를 할당 (필드, 메서드, 인터페이스 등)
4. 분석: 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
5. 초기화: 클래스 변수들을 적절한 값으로 초기화 (static 필드)

4. 실행엔진(Execution Engine)은 JVM 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행. 이때 실행 엔진은 두가지 방식으로 변경

1. 인터프리터: 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행. 
    - 하나하나의 실행은 빠르나, 전체적인 실행속도가 느린다는 단점이 있음
2. JIT 컴파일러(Just-In-Time Compiler): 인터프리터의 단점을 보완하기 위해 도입된 방식으로 바이트 코드 전체를 컴파일하여 바이너리 코드로 변경하고 이후에는 해당 메서드를 더이상 인터프리팅 하지 않고, 바이너리 코드로 직접 실행하는 방식. 
    - 하나씩 인터프리팅하여 실행하는 것이 아니라 바이트 코드 전체가 컴파일된 바이너리 코드를 실행하는 것이기 때문에 전체적인 실행속도는 인터프리팅 방식보다 빠름

 

[참고 자료]

- https://steady-snail.tistory.com/67

 

[JAVA] JVM 동작원리 및 기본개념

JAVA라는 언어를 통해 코딩을 하고 있는 사람으로서 JAVA의 간단한 탄생배경 그리고 JAVA의 시작과 끝이라고 할 수 있는 JVM을 한 번 짚고넘어가려고 해요 우선 JAVA의 탄생배경을 좀 알고가면 이해

steady-snail.tistory.com

- https://aljjabaegi.tistory.com/387

 

알기쉽게 정리한 JAVA의 컴파일과정 및 JVM 메모리 구조, JVM GC

알기쉽게 정리한 JAVA의 컴파일과정 및 JVM 메모리 구조, JVM GC 자바 개발자들이 간과 하기 쉬운 JAVA의 메모리 구조에 대해 포스팅 해보려고 합니다. 이와 관련하여 JAVA의 컴파일 과정과 Garbage Collec

aljjabaegi.tistory.com

- https://gyoogle.dev/blog/computer-language/Java/%EC%BB%B4%ED%8C%8C%EC%9D%BC%20%EA%B3%BC%EC%A0%95.html

 

[Java] 컴파일 과정 | 👨🏻‍💻 Tech Interview

[Java] 컴파일 과정 들어가기전 자바는 OS에 독립적인 특징을 가지고 있다. 그게 가능한 이유는 JVM(Java Vitual Machine) 덕분이다. 그렇다면 JVM(Java Vitual Machine)의 어떠한 기능 때문에, OS에 독립적으로

gyoogle.dev

 

'Lang > Java' 카테고리의 다른 글

[Java] Java 프로그래밍이란 무엇인가?  (0) 2022.09.30

프로그래밍이란 무엇인가?

프로그램은 어떤 값을 입력하고, 결과를 제공해주는 것이다.

프로그램을 만들려면 언어가 필요하다.

사람과 컴퓨터 사이에 소통하기 위한 언어를 '프로그래밍 언어'라고 하며 Java도 프로그래밍 언어 중 하나이다.

 

객체지향 프로그래밍 언어란 무엇인가?

자바와 같은 언어를 객체지향 언어라고 한다.

지금까지 대부분의 프로그래밍 언어들은 현실과 동떨어져 있었다. 하지만 객체지향 언어의 등장으로 현실 세계를 프로그램으로 표현할 수 있게 된다. 

class로 현실에 있는 사물 혹은 추상적인 것을 표현하게된다.

 

클래스란 무엇인가?

자바의 가장 작은 단위이다.

상태(state)와 행동(behavior)을 가지고 있다.

클래스 안에 변수를 선언하면 이를 상태라 하고 메소드를 선언하면 행동이라고 볼 수 있다.​

 

메소드란 무엇인가?

클래스 내에서 행동에 속하는 부분으로 특정한 작업을 수행하는 단위이다.

 

코드로 이해하기

/*형식*/
[접근 제어자] class [클래스 이름] {
	
	[접근 제어자] [리턴 타입] [메소드 이름] ([매개 변수]) {
	// 중간 내용
	}
}

public class Calculator{
	public int add(int num1, int num2){
    	 int sum;
         sum = num1 + num2;
         return sum;    
    }
}

 

 

'Lang > Java' 카테고리의 다른 글

[Java] JAVA 등장배경과 자바코드 실행과정  (0) 2022.09.30

Intro

코딩테스트를 준비하다보면 아래의 경우를 고려하여 완전 탐색해야하는 문제를 쉽게 만날 수 있습니다. 

1) 서로 다른 N개중 순서를 생각하지 않고 M개를 선택하는 경우의 수

2) 서로 다른 N개중 순서를 생각하지 않고 중복을 허용하여 M개를 선택하는 경우의 수 

3) 서로 다른 N개중 순서를 생각하고 M개를 선택하는 경우의 수

4) 서로 다른 N개중 순서를 생각하고 중복을 허용하여 M를 선택하는 경우의 수

따라서, 위의 경우를 효율적으로 탐색하기 위해서 시간 복잡도와 모듈을 활용한 탐색 방법을 정리해 두려고 합니다.

저는 주로 python3를 사용하여 ps하기 때문에 itertools 패키지 사용법과 함께 정리하겠습니다.

위의 경우를 순서대로 알아보겠습니다.

 

1) 조합 (combiantions)

시간복잡도: nCr = n!/(n-r)!r!

from itertools import combinations

nums = [1,2,3]
for i in range(1, len(nums)+1):
	print(list(combinations(nums,i)))
    
# [(1,), (2,), (3,)]
# [(1, 2), (1, 3), (2, 3)]
# [(1, 2, 3)]

2) 중복 조합

시간복잡도: nHr=n+r−1Cr = (n+r-1)! / r!

from itertools import combinations_with_replacement

nums = [1,2,3]
for i in range(1, len(nums)):
	print(list(combinations_with_replacement(nums, i)))
    
# [(1,), (2,), (3,)]
# [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

3) 순열

시간복잡도: nPr = n!/(n-r)!

from itertools import permutations

nums = [1,2,3]
for i in range(1, len(nums)):
	print(list(permutations(nums, i)))
    
# [(1,), (2,), (3,)]
# [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

4) 중복 순열

시간복잡도: n∏r = n^r

from itertools import product

nums = [1,2,3]
for i in range(1, len(nums)):
	print(list(product(nums, repeat = i)))
    
# [(1,), (2,), (3,)]
# [(1, 1), (1, 2), (1, 3),(2, 1), (2, 2), (2, 3),(3, 1), (3, 2), (3, 3)]

 

순열(permutations)

서로 다른 n개의 원소에서 r개를 중복없이 골라 순서대로 나열하는 경우의 수

순열 구현

def permutation(arr,r):
    arr = sorted(arr)
    used = [0 for _ in range(len(arr))]

    def generate(chosen, used):
        if len(chosen) == r:
            print(chosen)
            return

        for i in range(len(arr)):
            if not used[i]:
                chosen.append(arr[i])
                used[i] = 1
                generate(chosen, used)
                used[i] = 0
                chosen.pop()

    generate([],used)

조합(Combinations)

서로 다른 n개의 원소에서 r개를 뽑는 경우의 수

조합 구현

def combination(arr, r):
    arr = sorted(arr)
    used = [0 for _ in range(len(arr))]

    def generate(chosen):
        if len(chosen) == r:
            print(chosen)
            return

        start = arr.index(chosen[-1]) + 1 if chosen else 0
        for nxt in range(start, len(arr)):
            if used[nxt] == 0 and (nxt == 0 or arr[nxt-1] != arr[nxt] or used[nxt-1]):
                chosen.append(arr[nxt])
                used[nxt] = 1
                generate(chosen)
                chosen.pop()
                used[nxt] = 0
    generate([])

순열, 조합 파이썬 라이브러리

itertools

import itertools
pool = ['A','B','C']
print(list(map("".join, itertools.permutaions(pool, 2)))) # 순열
print(list(map("".join, itertools.combinations(pool, 2)))) # 조합

참고

  1. 힙(Heap) 이란?
  • 일종의 트리
  • 수의 집합에서 가장 작은 수(키)나 가장 큰 수만을 자주 꺼내올 때 유용한 자료구조
  • 배열에서는 최소값 탐색시 O(n), 힙에서는 O(logn)으로 시간복잡도에서 유리

 

  1. 힙의 구조
  • 트리 중 완전 이진 트리
  • 항상 자식은 두개 밖에 없다.
  • leaf의 가장 왼쪽부터 채운다
  • 키값의 대소 관계는 부모/자식 간에만 성립
  • 형제노드 사이에는 대소 관계가 정해지지 않는다

 

  1. 최소힙, 최대힙
  • 최소힙: 가장 작은 값이 루트
  • 최대힙: 가장 큰 값이 루트

 

  1. 힙의 구현
  • leftChild = parent*2
  • rightChild = parent*2+1
  • parent = child/2

 

  1. 파이썬에서 heapq 모듈 사용
  • import heapq : 파이썬 heapq 모듈은 heapq (prirority queue) 알고리즘 제공

 

  1. 힙 함수 활용하기
  • heapq.heappush(heap, iteam): item을 heap에 추가
  • heapq.heappop(heap): heap에서 가장 작은 원소를 pop & 리턴. 비어 있는 경우 IndexError가 호출됨.
  • heapq.heapify(x): 리스트 x를 즉각적으로 heap으로 변환함 (in linear time, O(N))

 

+ Recent posts