자바는 OS에 독립적인 특징을 가지고 있다.
JVM이 OS와 프로그램의 사이에서 기계어로 행석앻주는 역할을 하기 때문이다.

 

JVM JAVA Virtual Machine

컴파일된 바이트 코드를 기계가 이해할 수 있는 기계어로 변환

스택기반의 가상 머신 

메모리 관리와 GC를 수행

 

자바 코드의 실행 과정

1. 개발자가 자바코드를 작성한다. 

2. ( .java ) 인 파일을 자바 컴파일러를 통해 자바 바이트 코드로 컴파일한다.

3. 컴파일 된 바이트 코드를 JVM의 Class Loader에 전달한다. 

4. Class Loader는 동적 로딩을 통해 필요 클래스들을 로딩 및 링크하여 Runtime Data Area에 올린다. (JVM의 메모리)

5. 실행 엔진은 JVM메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행한다.

 

 

 

 

 

JVM
Class Loader + Runtime Data Areas + Execution Engine

 

 

 

 

Class Loader 특징

1. 계층 구조 

클래스 로더는 여러 클래스 로더끼리 부모-자식 관계를 이루어 계층적인 구조로 되어 있다.

 

  • 부트스트랩 클래스 로더 Bootstrap Class Loader
    • 최상위 클래스로더, 유일하게 JAVA가 아닌 네이티브 코드로 구현
    • JVM이 실행될 때 같이 메모리에 올라감
    • Object 클래스를 비롯하여 JAVA API들을 로드함

 

  • 익스텐션 클래스 로더 Extension Class Loader
    • 기본 JAVA API를 제외한 확장 클래스들을 로드함 - 다양한 보안 확장 기능 로드

 

  • 시스템 클래스 로더 System Class Loader
    • (부트 스트랩과 익스텐션 클래스로더가 JVM 자체의 구성 요소들을 로드한다) 시스템 클래스 로더는 어플리케이션의 클래스들을 로드함
    • 사용자가 지정한 $CLASSPATH 내의 클래스들을 로드함

 

  • 사용자 정의 클래스 로더 User-Defined Class Loader
    • 어플리케이션 사용자가 직접 코드 상에서 생성하여 사용하는 클래스 로더
    • WAS와 같은 프레임워크는 서로 독립적으로 동작하게 하기 위해서 이를 위해 사용자 정의 클래스 로더들을 사용하여 클래스 로더의 위임 모델을 통해 어플리케이션의 독립성을 보장함

 

2. 위임모델 

처음 바이트 코드를 넘겨받은 클래스로더.

필요한 클래스를 로드할 때 혹은 실행 엔진에서 명령어 단위로 바이트 코드를 실행하다가 처음으로 참조하는 클래스에 대해 클래스 로더에게 로드를 요청할 때.

로드를 요청받은 클래스 로더는 다음 순서대로 요청받은 클래스가 있는지 확인함

  1. 클래스 로더 캐시
  2. 상위 클래스 로더
  3. 자기 자신

이전에 로드된 클래스인지 클래스 로더 캐시를 확인한다.

없다면 상위 클래스 로더를 하나씩 거슬러 올라가며 확인하는데, 이 때 올라가는 도중에 클래스를 발견하더라도 부트 스트랩 클래스 로더(최상단 로더)까지 확인을 해서 부트 스트랩 클래스 로더에도 해당 클래스가 존재한다면 그 클래스를 로드하게 된다.

 

 

3. 가시성 제한

클래스 로더가 클래스 로드를 요청받았을 때 위임 모델에 의해서 클래스 로더 캐시를 확인하고 없으면 상위 클래스 로더를 확인한다. 이때 하위 클래스는 확인이 불가능한 특성이 바로 가시성 제한이다.

 

 

4. 언로드 불가

클래스를 로드하는 것은 가능하지만 그 반대로 unload하는 것은 불가능하다.

 

5. 이름 공간

네임 스페이스는 각 클래스 로더들이 가지고 있는 공간으로 로드된 클래스를 보관하는 공간

위임 모델을 통해서 상위 클래스 로더들을 확인해야할 때 확인하는 공간이 바로 이름 공간

네임 스페이스에 보관되는 기준은 FQCN Fully Qualified Class Name 으로 패키지 명까지 포함되어있는 식별자를 뜻한다.

 

각각의 클래스 로더가 각자 네임 스페이스를 가지고 있기 때문에 FQCN이 같은 클래스라도 네임스페이스가 다르면 다른 클래스로 갖주한다. ( 이 기능을 통해 로드한 클래스 로더를 제거하고 언로드의 효과를 줄 수 있다. )

 

 

클래스 로드 과정 

 

1. 로드 - 클래스 파일을 가져와서 JVM의 메모리에 로드한다.

2. 검증 - 클래스 로드 전 과정 중에서 가장 복잡하고 시간이 많이 걸리는 과정으로, 읽어들인 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 구성되어 있는지 검사한다.

3. 준비 - 클래스가 필요로 하는 메모리를 할당한다. 

4. 분석 - 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다.

5. 초기화 - 클래스 변수들을 적절한 값으로 초기화한다. (static 필드들은 설정된 값으로 초기화 )

 

 

 

 

 

 

런타임 데이터 영역 Runtime Data Area

JVM이 OS 위에서 실행되면서 할당받는 메모리 영역이 바로 런타임 데이터 영역이다.

이 영역은 크게 5가지, 세분화시 6가지로 나눠 볼 수 있다. 

PC 레지스터, JVM 스택, 네이티브 메서드 스택은 스레드마다 하나씩 생성되고 

힙, 메서드 영역은 모든 스레드가 공유해서 사용된다.

 

 

  • PC 레지스터 
    • Program Counter 레지스터는 현재 수행 중인 명령의 주소를 가지며 스레드가 시작될 때 생성되며, 각 스레드마다 하나씩 존재함
  • JVM 스택 
    • 스택 프레임이라는 구조체를 저장하는 스택
    • 예외 발생 시 printStackTrace( ) 메서드로 보여주는 Stack Trace의 각 라인 하나가 스택 프레임을 표현함
    • 스레드가 시작될 때 생성되며 각 스레드마다 하나씩 존재함
  • 네이티브 메서드 스택
    • 자바 외의 언어로 작성된 네이티브 코드를 위한 스택
    • JAVA Native Interface를 통해 호출하는 C/C++ 등의 코드를 수행하기 위한 스택으로, 언어에 맞게 스택 생성
    • 인스턴스 또는 객체를 저장하는 공간
    • Garbage Collection의 대상
    • JVM 성능 등의 이슈에서 가장 많이 언급되는 공간
    • 힙 구성 방식이나 가비지 컬렉션 방법 등은 JVM 벤더들의 재량
  • 메서드 영역 
    • 모든 스레드가 공유하는 영역
    • JVM이 시작될 때 생성된다.
    • JVM이 읽어 들인 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드와 메서드에 대한 정보, static 변수, 메서드의 바이트 코드들을 보관함
  • 런타임 상수 풀
    • JVM 동작에서 가장 핵심적인 역할을 수행하는 곳
    • 각 클래스와 인터페이스의 상수 뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블
    • 어떤 메서드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조한다.

 

 

 

 

 

실행 엔진 Execution Engine

실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행

 

바이트 코드와 각 명령어는 1바이트 크기의 OpCode와 추가 피연산자로 이뤄져있음

실행 엔진은 하나의 OpCode를 가져와서 피연산자와 작업을 수행한 다음 OpCode를 수행하는 식으로 동작한다.

 

이 수행 과정에서 실행 엔진은 바이트 코드를 기계가 실행할 수 있는 형태로 변경한는데 두가지 방법을 사용한다.

  • 인터프리터 
    • 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다. 하나하나의 해석은 빠르지만 전체적인 실행 속도는 느리다는 단점을 가진다. 
    • JVM 안에서 바이트 코드는 기본적으로 인터프리터 방식으로 동작한다.
  • JIT 컴파일러 Just-In-Time
    • 인터프리터의 단점을 보완하기 위해 도입된 방식
    • 바이트 코드 전체를 컴파일하여 네이티브 코드로 변경하고, 이후에는 해당 메서드를 더이상 인터프리팅 하지 않고 네이티브 코드로 직접 실행하는 방식
    • 전체적인 실행 속도는 인터프리팅방식보다 빠르다 ( 네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 캐시에서 바로 꺼내어 실행해 빠르게 수행된다 )
    • 하지만 JIT 컴파일러가 컴파일하는 과정은 바이트 코드를 하나씩 인터프리팅 하는 것보다 훨씬 오래 걸리기 때문에 JVM은 해당 메서드가 얼마나 자주 호출되고 실행되는 지를 체크하고, 일정 기준을 넘었을 때만 JIT 컴파일러를 통해 컴파일 하여 네이티브 코드를 생성한다.

 

 

 


참조

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

[JAVA] 객체 지향 프로그래밍(OOP)이란?  (0) 2021.08.11
[JAVA] 자바가상머신, JVM이란?  (0) 2021.08.11
[JAVA] HashMap과 HashTable의 차이점  (0) 2021.08.04
[JAVA] Hash란?  (0) 2021.08.04

항공기

 

⊙ AmsField

package ams;

public class AmsField {
	//				Key
	// 항공사, 항공기번호, 최대승객수, 출발지, 도착지
	String[][] arrPlane = new String[100][5];
	int insertCnt;
	int updateIndex;

	// 추가
	// 2차원 배열에 5개의 정보를 담기
	void insert(String[] arPlane) {
		arrPlane[insertCnt] = arPlane;
		insertCnt++;
	}

	// 수정
	void update(int index, String newValue) {
		boolean updateCheck = true;
		//항공사, 항공기번호, 승객수, 출발지, 도착지
		//0			1				2			3			4
		//index : 0, 1
			arrPlane[updateIndex][index+3] = newValue;
	}

	// 삭제
	void delete() {
	}

	// 검색
	String select(int index, String keyword) {
		int[] arIndex = null;
		int searchCnt = 0;
		String result = "";
		updateIndex = -1;
		
		for (int i = 0; i < insertCnt; i++) {
			if (keyword.equals(arrPlane[i][index])) {
				// i >> 행번호 : 검색할 비행기 번호
				searchCnt++;
				updateIndex = i;
				//바로 int배열에 담을 수 없기 때문에(검색 건수를 모르기 때문)
				//문자열에 검색된 행번호를 연결시켜 담는다.(", "는 구분점이다.)
				//구분점이 있어야지만 밑에서 각 값을 나눌 수 있기 때문이다.
				result += i + ", ";
			}
		}
		//검색 건수를 for문이 끝난 후에 알 수 있기 때문에
		//for문 밑에서 new 해준다.
		arIndex = new int[searchCnt];
		for (int i = 0; i < arIndex.length; i++) {
			//	result는 문자열이고 split() 사용시 
			//전체를 배열로 보기 때문에 그 뒤에 바로 []를 사용할 수 있다.
			arIndex[i] = Integer.parseInt(result.split(", ")[i]);
		}
		//list(int[] arIndex)메서드에 값을 전달하고 list(int[] arIndex)에서 리턴된
		//결과값을 select()에서 리턴한다.
		return list(arIndex);
	}

	// 목록
	String list() {
		String result = "항공사, 항공기번호, 최대승객수(명), 출발지, 도착지\n";
		for (int i = 0; i < insertCnt; i++) {
			result += "♥";
			for (int j = 0; j < arrPlane[0].length; j++) {
				result += arrPlane[i][j];
				result += j == arrPlane[0].length - 1 ? "" : ", ";
			}
			result += "\n";
		}

		if (insertCnt == 0)
			result = "목록 없음";

		return result;
	}
	
	String list(int[] arIndex) {
		String result = "항공사, 항공기번호, 최대승객수(명), 출발지, 도착지\n";
		for (int i = 0; i < arIndex.length; i++) {
			result += "♥";
			for (int j = 0; j < arrPlane[0].length; j++) {
				result += arrPlane[arIndex[i]][j];
				result += j == arrPlane[0].length - 1 ? "" : ", ";
			}
			result += "\n";
		}

		if (arIndex.length == 0)
			result = "검색 결과 없음";

		return result;
	}
	
}

 


 

⊙ AmsMain

package ams;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

public class AmsMain {
	public static void main(String[] args) {
		String title = "항공기 관리 프로그램";
		String[] menu = {"추가하기", "검색하기", "수정하기", "삭제하기", "목록보기"};
		String[] updateMenu = {"출발지 수정", "도착지 수정"};
		String[] updateMsg = {"출발지", "도착지"};
		AmsField af = new AmsField();
		ImageIcon icon = new ImageIcon("src/img/main.gif");
		int choice = 0;
		String[] arPlane = new String[5];
		String keyword = "";
		
		while(true) {
			choice = JOptionPane.showOptionDialog(null, "", title, JOptionPane.DEFAULT_OPTION,
					JOptionPane.PLAIN_MESSAGE, icon, menu, null);
			
			if(choice == -1) break;
			
			switch(choice) {
			//추가
			//항공사, 항공기번호, 최대승객수(명), 출발지, 도착지
			case 0:
				//5개의 값을 ", "로 구분하여 한번에 입력하기
				//split("구분점")은 리턴타입이 문자열 배열이다.
				//구분점이 있다는 것은 최소한 값이 2개 이상이기 때문에 배열로 리턴한다.
				arPlane = ("" + JOptionPane.showInputDialog(null,
						"항공사, 항공기번호, 최대승객수(명), 출발지, 도착지", title, JOptionPane.PLAIN_MESSAGE,
						icon,	null, null)).split(", ");
				
				af.insert(arPlane);
				break;
			//검색
			case 1:
				break;
			//수정
			case 2:
				String newValue = "";
				
				choice =JOptionPane.showOptionDialog(null, "", title, JOptionPane.DEFAULT_OPTION,
						JOptionPane.PLAIN_MESSAGE, icon, updateMenu, null);
				
				if(choice == -1) break;
				
				keyword = "" + JOptionPane.showInputDialog(null,
						"수정하실 항공기 번호를 입력하세요", title, JOptionPane.PLAIN_MESSAGE,
						icon,	null, null);
				
				if(af.select(1, keyword).equals("검색 결과 없음")) {
					JOptionPane.showMessageDialog(null, "수정 실패");
				}else {
					newValue = "" + JOptionPane.showInputDialog(null,
							"새로운 " + updateMsg[choice] + "를 입력하세요", title, JOptionPane.PLAIN_MESSAGE,
							icon,	null, null);
					af.update(choice, newValue);
					JOptionPane.showMessageDialog(null, "수정 성공");
				}
				
				break;
			//삭제
			case 3:
				break;
			//목록
			case 4:
				JOptionPane.showMessageDialog(null, af.list());
				break;
			}
		}
	}
}

 

생성자의 목적

1. 힙 메모리 영역에 클래스 필드를 생성해주는 목적

2. 초기화 역할

 

메서드의 모양 :  name ( )

생성자의 모양 : class name ( )

 

위처럼 생성자도 메서드와 생김새가 비슷하네?

생성자도 메서드다

그렇다면 왜 생성자는 메서드라고 하지 않을까?

 


 

생성자는 메서드의 기능과 똑같지만 return이 없기 때문에 메서드라고 부르지 않는다.

 


클래스를 만드는 순간 굳이 생성자를 일부를 만들어주지 않아도 기본 생성자라는 것이 생기는데, 보이진 않지만 내부적으로 올라간 것이다.

따라서 선언없이 사용이 가능하다.

 

기본 생성자

1. 클래스 선언 시 자동으로 생성된다.

2. 사용자가 직접 선언하지 않아도 사용 가능하다.

3. 사용자가 직접 생성자를 선언하는 순간 그것을 기본 생성자로 여겨서 따로 기본 생성자가 생기진 않는다.

 


< 변수 >

매개변수 : { } 안, 닫는 중괄호를 만날 때 끝난다

매개 변수와 지역변수는 stack에 저장이된다.

전역 변수는 data 영역에 저장된다.

 


오버 로딩 Overloading

매개변수의 개수나 타입이 다를 때, 같은 이름으로 선언할 수 있다.

         →  메서드의 이름은 같으나 매개변수의 갯수 혹은 타입이 다르면 선언 가능

load : 나갔다가 다시 불러올 때

over : 넘치게

overload : 넘치게 불러온다 (같은 이름이라) 

 

《 오버 로딩은 메서드의 첫 번째 기능 ≫

 


Car와 Road를 만들어보자

 

package studyalone;

public class Car {
	//Car 클래스는 브랜드, 색, 가격, 비번을 갖고 있다
	String brand;
	String color;
	int price;
	String pw="1122";
	
	//생성자 , 비번 새로
	public Car(String b, String c ,int p,String pw) {
		this.brand=b; this.color=c; this.price=p;this.pw=pw;
	}
	
	//생성자 , 초기 비번 그대로
	public Car(String b, String c ,int p) {
		this.brand=b; this.color=c; this.price=p;
	}
	
	//외부에서 비밀번호 입력받기
	//입력받은 비밀번호와 자동차의 비밀번호를 비교하기
	//비밀번호가 일치한다면 시동 켜주기
	//이미 시동이 켜져있다면 "시동이 이미 켜져있습니다"출력
	//이미 시동이 꺼져있다면 "시동이 이미 꺼져있습니다"출력
	//비번 3회 오류 시 경찰 출동
	
	boolean isOn=false;
	int policeCnt;
	
	//시동키는 메서드
	boolean engineStart(String pw ){
		boolean policeCheck=false;
		
		if(this.pw.equals(pw)) {
			if(!isOn) {
				System.out.println(this.brand+" 시동 킴");
				isOn=true;
				policeCnt=0;
			}else {
				System.out.println(this.brand+" 시동이 이미 켜져있음");
			}
		}else {
			policeCnt++;
			if(policeCnt==3) {
				System.out.println("경찰 출동");
				policeCheck=true;
			}else {
				System.out.println("비밀번호 오류");
			}
		}
		return policeCheck;
	}
	
	
	//시동끄는 메서드
	void engineStop(){
		if(!isOn) {
			System.out.println(this.brand+" 시동 끔");
		}else {
			System.out.println("시동이 이미 꺼져있습니다.");
		}
	}
	
	
	//자동차 정보 출력 메서드
	void show() {
		System.out.println(brand+", "+color+", "+price+"만원");
		//지역변수가 같은 이름이 아니라서 굳이 this.brand등등으로 하지 않은 것
	}

	
}
package studyalone;

import java.util.Scanner;

public class Road {
	public static void main(String[] args) {
		Car myCar=new Car("벤틀리","blue",1500,"981122");
		
		String menu= "1. 시동 켜기\n2. 시동 끄기";
		Scanner sc =new Scanner(System.in);
		int choice=0;
		String tryPw="";
		
		myCar.show();
		
		//무한반복 
		//시동을 한번이라도 킨 후 시동을 끄면  break
		while(true) {
			System.out.println(menu);
			
			choice=sc.nextInt();
			
			if(choice==1) {
				System.out.println("비밀번호를 입력하세요");
				tryPw=sc.next();
				
				if(myCar.engineStart(tryPw))
					break;
			}else if(choice==2) {
				//시동을 한번이라도 켜기 전엔 isOn이 false니까 break안함
				//한번이라도 시동을 겼다면 isOn은 true니까 break함
				if(myCar.isOn) {
					myCar.engineStop();
				}else {
					myCar.engineStop();
				}
			}
		}
		
	}
}

 

alt shift n 새로운 프로젝트 만들기
ctrl n 새로운 클래스 만들기 ( 해당 프로젝트의 src 선택 후 )
alt ㅁ 프로젝트, 클래스, 인터페이스에서 해당 이니셜로 선택할 수 있음
ctrl shift + 글씨 키우기
ctrl shift - 글씨 줄이기
ctrl m 전체화면
insert 쓰는것이 겹쳐지면서 지워지게하는 커서
alt shift s o 생성자 만들기
alt shift r 전체적으로 이름 바꾸기 ( class 이름도 )
ctrl space바 자동완성
ctrl z 뒤로가기
ctrl s 저장
ctrl x 저장하고 해당 영역 삭제
ctrl t 가족 관계도
ctrl shift o 자동 import
ctrl shift f 줄 맞춤
alt 위 아래 해당영역 위 또는 아래로 이동
ㅊ한자 제곱근이나 1/2같은 기호 사용

 


 

전역 변수는 data 영역에 저장된다.

전역변수는 자동 초기화가 된다.

 


%f 를 쓰면 기본적으로 소수점 6자리가 나온다.

float는 4바이트이자 32비트다.

 

3.7이란 숫자를 float로 저장한다고 하면

1bit 8bit 23bit
양수면 0 / 음수면 1 exp 진수 영역 f 가수 영역
3.7은 양수니까 0 3.7의 정수는 3이니까 0011

3.7의 0.7이 가수영역이니까 이진수로 교환 // 가수영역 * 2 한 값이 1보다 크면 1을 넣고 (계산값-1)*2를 해준다. 1.0이 되는 순간까지 무한 반복을 하거나 24비트에서 반올림 된다.

0.7*2=1.4 ---> 1써짐 ---> 1.4-1=0.4

0.4*2=0.8 ---> 0써짐

0.8*2=1.6 ---> 1써짐 ---> 1.6-1=0.6

           . . .

0 0000 0011 10110...

 

이런 식으로 비트가 반올림되는 컴퓨터 연산의 오류가 있다.

 

그래서 7자리 까진 정확하지 않고 기본적으로 6자리까지 정확하다.

따라서 %f 는 기본적으로 6자리까지 출력이 되는 것이다.

 


eclipse를 처음 깔면 perspective를 java로 선택이 되어있는지 확인해야 한다.

java로 선택해놓으면 package explorer이 켜지는 것이다.

+ Recent posts