[Java] JVM, Static/Non-Static, Branch Process

Jay Mild Lee·2022년 11월 10일
0

Java

목록 보기
2/10
post-thumbnail

I. JVM(Java Virtual Machine)

1. JVM이란?

JVM은 Java 프로그램의 실행 환경을 구성하는 소프트웨어로, 컴파일된 바이트 코드(.class)를 운영체제에 맞춰 실행하는 역할을 한다. 이러한 특징은 Java의 높은 이식성으로 이어지는데, 어떤 운영체제에서 컴파일을 하던 동일한 바이트 코드(.class)를 생성하며 이러한 바이트 코드를 운영체제에 맞는 JVM으로 실행하기만 하면 되기 때문이다.

비교를 위해 C로 작성된 프로그램을 가정해보자.

윈도우에서 컴파일된 프로그램은 윈도우에서만 실행되며, 리눅스에서 컴파일된 파일은 리눅스에서만 실행된다. 만일 윈도우에서 개발한 프로그램을 리눅스에서 사용하고자 한다면, 리눅스 환경으로 크로스 컴파일을 하여 새로운 실행파일을 만들어야 한다.

Java로 작성된 프로그램의 컴파일 및 실행과정은 다음과 같다.

Java로 작성된 프로그램의 경우 컴파일된 .class 파일을 사용하며, Window와 Linux 양쪽에서 모두 사용 가능 하다. JVM의 차이만 있을 뿐, 동일하게 java 명령을 통해 실행 가능하다.

2. JVM의 구조

1) Class Loader

Class Loader는 컴파일된 .class 파일을 통합하여 JVM이 운영체제로부터 할당받은 메모리 영역, 즉 Runtime Data Area의 Method에 적재한다.

2) Execution Engine

적재된 .class 파일들은 JVM에 의해 Execution Engine에 제공된다. 제공된 .class 파일들은 Execution Engine에 의해 명령어 단위로 해석되어 실행된다.

3) Garbage Collector

Heap 메모리 영역에 적재된 객체들 중 참조되지 않는 객체들을 탐색 및 제거한다. 제거하는 동안 Garbage Collector를 수행하는 스레드를 제외한 나머지 스레드들은 정지된다.

4) Method Area (모든 thread가 공유)

클래스 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보와 같은 각종 필드 정보들과 메서드 정보, 데이터 Type정보, Static 변수, final class 등이 생성되는 영역

5) Heap Area (모든 thread가 공유)

new 키워드로 생성된 객체와 배열이 생성되는 영역. 주기적으로 Garbage Collector에 의해 제거된다.

6) Stack Area

지역변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값들이 생성되는 영역.

7) PC register

thread가 생성될 대마다 생성되는 영역으로, program counter를 저장한다.

8) Native Method Stack

  1. Java 이외의 언어로 작성된 네이티브 코드를 사용할 때 사용되는 영역.

II. Static과 Non-Static

1. Static

Static은 JVM Runtime Area의 Method Area에 위치하며, Class Variable로 분류된다. 모든 Static 변수들은 Class Loader에 들어온 이후 메모리를 할당받으며, 할당 받은 이후 값이 배정된다. 이 때 할당 받는 공간이 Method Area이다.

Static이 위치한 Method Area는 모든 Thread가 공유하기 때문에, new를 통해 객체로 만들 필요 없이 [Class.Name]으로 접근이 가능하다. 하지만 프로그램이 시작할 때부터 끝날 때까지 계속 그 자리에서 메모리를 점유하기 때문에, 남용해서는 안된다.

2. Non-static

Non-Static은 Method Area가 아닌 Heap 영역 메모리에 생성된다. 메모리의 관리 차원에서 매우 유용하다.

III. Static 예시(Class)

1. 요구사항

  • 계좌의 입금, 출금, 잔액 확인 및 프로그램 종료 기능이 있는 Class를 구현해라.
  • 이 때, 테스트를 위해 메소드들을 동시에 사용할 수 있도록 Class는 어떤 thread에서라도 접근 가능해야 한다.

2. 코드

/*filename: divert/Account.java*/
package basicGrammar.divert;
import java.util.Scanner;

public class Account {
    // field
    static int balance;

    // constructor
    public Account(){
        balance = 0;
    }
    // method
    // 1. method for deposit
    static public void deposit(){
        Scanner scanner = new Scanner(System.in);
        System.out.print("예금액> ");
        int credit = scanner.nextInt();
        balance = balance + credit;
    }
	// 2. method for withdraw
    static public void withdraw(){
        Scanner scanner = new Scanner(System.in);
        System.out.print("출금액> ");
        int credit = scanner.nextInt();
        if (balance - credit < 0){
            System.out.println("잔액이 부족합니다.");
            return;
        }
        balance = balance - credit;
    }
	// 3. method for print balance
    static public void getBalance(){
        System.out.printf("잔액> %d\n", balance);
    }
	// 4. method for shutdown program
    static public void killProcess(){
        System.out.println("프로그램 종료.");
        System.exit(0);
    }
}

III. Branch Process

Java의 대표적인 분기 방식은 If-Else, Switch-Case가 있으며, Enum 또한 비슷하게 활용할 수 있다. 아래 예시들은 위 Class 예시를 활용한 코드들이다.

1. If

/*filename: divert/byIf/DivertByIf.java*/
package basicGrammar.divert.byIf;
import basicGrammar.divert.Account;

import java.util.Scanner;

public class DivertByIf {
    public static void main(String[] args) throws Exception {
        boolean run = true;
        Scanner scanner = new Scanner(System.in);


        while(run){
            System.out.println("-----------------------------------");
            System.out.println("1.입금 | 2. 출금 | 3. 잔액 | 4. 종료");
            System.out.println("-----------------------------------");
            System.out.print("선택> ");

            int tmp = scanner.nextInt();
            scanner.nextLine();

            if (tmp == 1){
                Account.deposit();
            }
            else if (tmp == 2) {
                Account.withdraw();
            }
            else if (tmp == 3) {
                Account.getBalance();
            }
            else if (tmp == 4) {
                Account.killProcess();
            }
            else{
                System.out.println("1~4 사이의 숫자를 입력하세요.");
            }
        }
    }
}

2. Switch

/*filename: divert/bySwitch/DivertBySwitch.java*/
package basicGrammar.divert.bySwitch;

import basicGrammar.divert.Account;

import java.util.Scanner;

public class DivertBySwitch {
    public static void main(String[] args) throws Exception {
        boolean run = true;
        Scanner scanner = new Scanner(System.in);

        while(run){
            System.out.println("-----------------------------------");
            System.out.println("1.입금 | 2. 출금 | 3. 잔액 | 4. 종료");
            System.out.println("-----------------------------------");
            System.out.print("선택> ");

            int tmp = scanner.nextInt();
            scanner.nextLine();

            switch(tmp){
                case 1:
                    Account.deposit();
                    break;

                case 2:
                    Account.withdraw();
                    break;

                case 3:
                    Account.getBalance();
                    break;

                case 4:
                    Account.killProcess();
                    break;

                default:
                    System.out.println("1~4 사이의 숫자를 입력하세요.");
                    break;

            }
        }
    }
}

3. Enum

1) Main Code

/*filename: divert/byEnum/DivertByEnum.java*/
package basicGrammar.divert.byEnum;

import java.util.Scanner;

public class DivertByEnum {
    public static void main(String[] args) throws Exception {
        boolean run = true;

        Scanner scanner = new Scanner(System.in);

        while(run){
            System.out.println("-----------------------------------");
            System.out.println("1.입금 | 2. 출금 | 3. 잔액 | 4. 종료");
            System.out.println("-----------------------------------");
            System.out.print("선택> ");

            int opnum = scanner.nextInt();
            scanner.nextLine();
            Banking bank = Banking.of(opnum);
            bank.operation();
        }
    }
}

2) Enum Code

/*filename: divert/byEnum/Banking.java*/
package basicGrammar.divert.byEnum;

import basicGrammar.divert.Account;
import java.util.Arrays;

public enum Banking {
    DEPOSIT(1){
        @Override
        public void operation(){
            Account.deposit();
        }
    },
    WITHDRAW(2){
        @Override
        public void operation(){
            Account.withdraw();
        }
    },
    BALANCE(3){
        @Override
        public void operation(){
            Account.getBalance();
        }
    },
    QUIT(4){
        @Override
        public void operation(){
            Account.killProcess();
        }
    };

    private final int operationCode;


    // 생성자
    Banking(int operationCode){
        this.operationCode = operationCode;
    }
    // code가 Enum의 operationCode와 일치하는 Banking을 리턴 
    public static Banking of(int code) {
        return Arrays.stream(Banking.values())
                .filter(opcode -> opcode.operationCode == code)
                .findAny()
                .orElseThrow(() -> new IllegalArgumentException("해당하는 Operation Code가 존재하지 않습니다."));
    }

    public abstract void operation();
}

0개의 댓글