본문 바로가기
java

[Java] 레퍼런스 타입, jvm 메모리 운영, 프로그램-프로세스-쓰레드 등

by jinbro 2017. 9. 7.
[들어가기에 앞서]
- 기본적으로 필요한 개념까지 살펴봄 : JVM 메모리 구조, 프로그램 - 프로세스 - 쓰레드
- 이번 파트는 제대로 몰랐다는 생각이 강해서 구체적으로 내용 정리해봄


[자바의 데이터타입]
- 프로그램 : 데이터를 처리함
- 데이터의 타입
(1) primitive type : 정수, 실수, 논리리터럴을 저장하는 타입(+ 관련 연산)
=> 변수에 실제 값을 저장
=> 메모리 구조에서 스택 영역에 생성

(2) refrerence type : 객체의 주소값을 참조하는 타입(array, enum, class, interface)
=> 변수에 참조하는 객체의 주소값(메모리)을 저장
=> 객체 주소값을 가지고 객체를 참조한다는 뜻
=> 메모리 구조에서 힙 영역에 생성 : 아래 참고!
=> 변수 자체는 스택에 생성되고 변수의 값으로 저장된 주소값을 가지고 힙 영역의 객체를 참조하는 형태


[JVM와 메모리]
- JVM(java.exe)이 실행되면 운영체제에서 할당받은 메모리 영역(Runtime Data Area) 아래와 같이 구분해서 사용

(1) 메소드 영역
- JVM 시작될 때 생성, 모든 스레드 공유 영역
- 코드 영역에서 사용되는 클래스들을 클래스 로더로 읽어 클래스 별로 저장 : 컴파일러가 .class로 변환(바이트코드)
- 런타임 상수풀, 필드 데이터, 메소드 데이터, 메소드 코드, 생성자 코드 등 분류해서 저장


(2) 힙 영역
- 객체와 배열이 생성되는 영역
- JVM 스택 영역의 변수, 다른 객체의 필드에서 참조함
- 힙 영역에 생성되어있지만 스택 영역, 다른 객체에서 참조하지않으면 가비지컬렉터를 실행시켜 힙 영역에서 제거시킴


(3) JVM 스택 영역
- 각 쓰레드마다 하나씩 존재, 쓰레드가 시작할 때 할당
- 메소드를 호출할 때 마다 프레임을 추가하고, 종료되면 해당 프레임을 제거 : js의 실행컨텍스트
=> 블록 단위 스코프 생성 : 메인, 객체의 메서드 -> 블록을 벗어나는 순간 프레임 스택에서 제거(아래 예시 참조)
=> 스택 영역에 저장된 변수 중 객체, 배열 참조변수는 힙 영역에 저장된 것의 주소를 값으로 가짐 : 스택 저장

- printStackTrace() : try ~ catch(e)로 예외처리해둔 부분에서 스택 에러 발생 시 에러를 상세히 띄움(Log)
- 자바 프로그램에서 추가적인 쓰레드를 생성하지않는다면 main 쓰레드만 존재, JVM 스택도 1개만 존재
=> 프로그램, 프로세스, 쓰레드를 알지못한다면!
1) 프로그램
- 실행 가능한 단위
- 보통 응용어플리케이션을 말함
- 보조기억장치에 저장 : HDD, SSD

2) 프로세스
- 메모리(주기억장치)상에 올라가있는 상태 : 실행된 상태, 실행되는 작업 단위
- 실행 중인 프로그램 인스턴스
- 프로세스는 프로세스 개별 주소 공간과 파일, 메모리, 쓰레드를 소유함
- 보통 1개의 쓰레드(메인쓰레드)를 생성 : 모든 명령을 처리함
=> main() 함수에서 명령어 처리 : 메서드 호출 및 변수 선언, 연산

3) 쓰레드
- 프로세스 내에서 실행되는 흐름의 단위를 말함 : 흐름을 통해서 주고 받음(명령어 처리 요청 -> 연산 -> 응답)
- 하나의 프로세스는 여러개의 쓰레드를 소유할 수 있음 : 멀티쓰레드(하나의 프로세스 안에 존재하는 쓰레드들은 같은 코드, 주소공간 공유)
=> 비동기처리 : 하나의 프로세스에서 멀티쓰레드를 소유하도록해서 명령 흐름처리를 여러 갈래로
=> 자원낭비가 있을 수 있음 : 주소공간 공유
=> 스케쥴링 가능 : 쓰레드 우선순위
=> 동기화 제공 : 멀티쓰레드의 경우 같은 객체를 참조할 경우가 발생할 수 있으므로 다른 쓰레드가 변경할 수 없도록
=> 쓰레드 상태 제공 및 제어 가능

- 프로세스는 껍데기일 뿐 쓰레드가 모든 명령을 수행함


[reference type]
(1) 위의 내용 정리부터
- 객체와 배열은 힙 영역에 저장되고, 이를 참조하는 변수는 스택 영역에 저장됨
- 힙 영역에 저장된 객체나 배열을 스택 영역의 변수에서 참조하지않으면 가비지컬렉터가 힙 영역에서 제거
- 힙 영역에 저장된 객체, 배열을 참조하는 변수는 실제로 스택 영역에서 값을 객체, 배열의 힙 영역 주소값을 가짐
=> 객체, 배열 간에 ==, != 연산 : 변수에 저장한 주소값 비교 - 동일한 객체, 배열을 참조하고 있는지 비교
=> primitive type 변수 비교 : 고유 메모리 공간에 저장된 값 비교하는 것 같음 - primitive는 실제 값을 저장


(2) null
- 참조 타입 변수가 힙 영역의 객체를 참조하지않는다는 것
=> 가비지컬렉터 출동

- 초기화할 때 null 선언 가능 : 스택 영역에 생성
- NullPointerException 조심할 것 : null 체크해주기
=> 참조하는 객체가 없는데 변수로 컨트롤 하려할 때 발생함


(3) 종류 : 사용자정의는 설명 제외
- String : 변수 선언 - 힙 영역 String 객체 주소값(스택), String 객체 생성 - 문자열(힙)
=> 문자열 리터럴이 동일하다면 같은 객체 주소값 참조
=> new로 String 새로운 객체를 생성할 경우 같은 문자열을 생성자로 넘겨주더라도 다른 객체 참조
=> 문자열만 비교하는 메서드 제공함 : String equals()


- Array : 변수는 하나의 데이터를 저장, 그럼 필요한 만큼 변수를? 놉
=> 같은 타입 데이터만 저장, 배열 선언할 때 어떤 데이터타입의 배열인지 같이 선언 - int arr[]
=> primitive type Array : 각 항목에 직접 값을 가짐(스택)
=> reference type Array : 각 항목에 객체의 번지를 가짐(스택 - 힙)
=> 배열 길이 지정 : 단점이 될 수 있음, 확장성을 가지지않음
=> 배열 복사하기 : 배열 길이는 한번 지정하면 변경x -> 변경하기위한 방법
- 새로운 배열 생성 : 길이 지정, 아래 예제코드 참고
(1) 이전 배열과 새롭게 생성한 배열 for문 돌리기
(2) System.arraycopy 사용하기 : ctrl + 메서드명 클릭(intelliJ 기준) - 인자값 뭘 전달해야하는지
(3) reference type Array의 경우 1번은 얕은복사(같은 객체 참조), 새로운 객체 생성 참조 -> 깊은복사

=> 자주 발생하는 예외 : ArrayIndexOutOfBoundException - 배열 인덱스X
=> 선언과 배열 생성을 한번에 해야함 : 선언 따로 생성 따로하는 방법 정해져있음(예제 참고)
=> 메서드의 인자로 배열을 넘길 때에도 new 타입[]{원소}로 넘겨야함
=> 임의 공간 생성 : new 타입[길이]; - 배열 요소를 미리 선언하지않고 메모리 공간만 생성해둠(타입 메모리크기 X 길이)
=> 임의 공간 생성 : 공간만 확보해놓을 경우 기본값(0)으로 초기화함 - 타입마다 다름(참조형: null, 정수, 실수, 논리: false)
=> 임의 공간 생성 : 공간 확보해놓고 값을 저장할 때에는 배열변수명[인덱스값] 으로 위치를 찾아서 저장해줌
=> 생성하는 배열은 Array의 인스턴스 : 배열 길이 멤버변수(length, 읽기전용)
=> static void main(String[] args) : JVM은 길이 0 String 배열 생성 후, main 메서드를 호출할 때 전달함
=> static void main(String[] args) : 배열의 용도 - 커맨드라인을 통해 입력된 데이터 수(length) + 데이터
=> static void main(String[] args) : Run > Edit Configurations > Program arguments 파라미터 설정(IntelliJ)
=> 다차원 배열 : 1차원 배열이 서로 연결된 구조 - 0번째 배열의 1번째 요소(행: 0, 열: 1)
=> 다차원 배열 : new 타입[행][열]
=> 다차원 배열 : 행의 첫번째 요소(열) 객체 생성(1) + 행의 전체 요소 객체 생성(행 길이만큼, 크기는 열 길이만큼)
=> 향상된 for문 : 자바5부터 배열/컬랙센객체(뒤에서 배움) 조금 더 쉽게 처리할 목적으로 제공
=> 향상된 for문 : 카운터 변수, 변수 증감식을 사용x(개발자 임의지정x)
=> 향상된 for문 : for(배열타입변수: 배열) - for(int score: scores)
=> 향상된 for문 : 요소값이 존재하는지 판단하고 변수에 저장하고 실행문 실행 - 예제 코드 참고


- Enum(열거타입) : 몇가지 한정된 값만 가지는 데이터 타입, 서로 연관된 상수의 집합 정의할 때
=> 열거형을 의미하는 특별한 형태 클래스
=> enumeration : 열거
=> 상수 : 변수처럼 변하지않음, 한번만 생성해둠, 컴파일 타임에서 모든 값 지정, 동적할당X
=> final static 변수명 = 값, 그리고 비교한 후 원하는 문자로 출력 하는 방식 탈피
private static int MONDAY = 1;
private static int TUESDAY = 2;

public static void main(String[] args){
int day = MONDAY;

switch(day){
case MONDAY:
System.out.println("월요일입니다.");
break;
}
}

=> 상수에 대입된 값이 겹칠 때 난감
private static int MONDAY = 1;
private static int TUESDAY = 2;

private static int JANUARY = 1;
private static int FEBRUARY = 2;

=> enum 생성 : public enum 열거타입명 { 몇개의 열거상수(상수 데이터) }
public enum 열거타입명 {
열거상수1,
열거상수2,
열거상수3
}

=> enum 사용 : 열거타입 열거타입변수명 = 열거타입.열거상수; (열거타입변수에는 null 저장가능 : 열거타입도 참조형)
=> enum 사용예시 : Week today = Week.MONDAY;
=> enum 사용설명 : 참조타입 변수 - 객체의 주소값을 저장, 그렇다면 열거타입.열거상수도 객체?(맞음: 열거상수를 가리키는 객체)
=> enum 사용설명 : 열거상수의 개수만큼 열거타입.열거상수(객체)를 생성함 - 메모리 메소드 영역에 생성됨
=> enum 사용설명 : 열거타입.열거상수 == 열거상수 주소값을 가진 객체, 열거타입변수 - 열거상수 주소값을 저장(참조변수 = 은 객체주소값 저장)
=> enum 최종설명 : 열거상수(객체, 메소드영역 : 코드에서 사용되는 class - enum도 클래스), 열거타입변수(스택영역), 열거타입.열거상수(힙영역)
=> 알아둬야할 점
(1) enum 열거타입명 : 첫글자 대문자
(2) enum 열거상수(객체) : 대문자
(3) 메모리의 메소드영역 : 정적인 영역(상주함)


[예제코드]
import java.lang.reflect.Array;
import java.util.Calendar;
import java.util.Scanner;

public class Reference {

public static void main(String[] args) {

/* Run > Edit Configurations > Program Arguments > 파라미터 설정 */
if(args.length != 2){
System.out.println("전달된 매개변수 개수가 2개가 아닌데");
System.exit(0);
}

System.out.println(Integer.parseInt(args[0]));



/* Stack */
int a = 5; // stack -> a = 5

if(a==5){
int b = 6;
int c = 7; // stack -> a = 5, b = 6, c = 7
}

int d = 8; // stack -> a = 5, d = 8


/* e.printStackTrace
try{
int num = 0;
int num2 = 5;

System.out.println(num2/num);

//throw new Exception(); 강제 예외발생
} catch (Exception e){
e.printStackTrace();
}*/


/* 참조타입 비교 : 참조하고있는 객체주소 비교 */
String str = "안녕하세요";
String str2 = str;
System.out.println(str == str2);


/* String : 같은 문자열 리터럴이면 같은 주소값 참조 */
String str3 = "jinbro";
String str4 = "jinbro";
System.out.println(str3 == str4);

String str5 = new String("jinbro");
System.out.println(str3 == str5);
System.out.println(str5.equals(str3));


/* 배열 선언, 생성 따로 하는 방법이 정해져있음
int arr[];
arr = {1,2,3,4,5}; */

int arr[];
arr = new int[]{1,2,3,4,5};


int result = add(new int[]{1,2,3,4,5});
System.out.println(result);


/* array copy(1) */
int oldArr[] = {1,2,3};
int newArr[] = new int[5];

for(int i=0; i<oldArr.length; i++){
newArr[i] = oldArr[i];
}


/* array copy(2) */
int newArr2[] = new int[6];
System.arraycopy(oldArr, 0, newArr2, 0, oldArr.length);

for(int i=0; i<newArr2.length; i++){
System.out.println("newArr2[" + i + "] : "+ newArr2[i]);
}

oldArr = null;


/* new Array default initialize */
char arr2[] = new char[5];
System.out.println(arr2[3]);

int arr3[] = new int[5];
System.out.println(arr3[3]);

String arr4[] = new String[5];
System.out.println(arr4[3]);

double arr5[] = new double[10];
for(int i=0; i<arr5.length; i++){
arr5[i] = i; //자동형변환
}
//System.out.println(arr5[11]); ArrayIndexOutOfBoundException


/* reference type Array */
String[] strArray = new String[3];
strArray[0] = "JAVA";
strArray[1] = "JAVA";
strArray[2] = new String("JAVA");
System.out.println(strArray[0] == strArray[2]); //false
System.out.println(strArray[0].equals(strArray[2])); //true


/* for */
int scores[] = {96, 72, 93, 91, 88};
int sum = 0;
for(int score: scores){
sum += score;
}
System.out.println(sum);
System.out.println("평균 : " + (double)sum/scores.length);


/* enum */
Week today = Week.월요일;
System.out.println(today == Week.월요일);

switch (today){
case 월요일:
System.out.println("월요일입니다");
break;

case 화요일:

break;

case 수요일:

break;

case 목요일:

break;

case 금요일:

break;

case 토요일:

break;

case 일요일:

break;
}


/* Calendar + enum */
Calendar now = Calendar.getInstance();
Week today2 = null;

int week = now.get(Calendar.DAY_OF_WEEK);

switch (week){
case 1:
today2 = Week.일요일;
break;

case 2:
today2 = Week.월요일;
break;

case 3:
today2 = Week.화요일;
break;

case 4:
today2 = Week.수요일;
break;

case 5:
today2 = Week.목요일;
break;

case 6:
today2 = Week.금요일;
break;

case 7:
today2 = Week.토요일;
break;
}

System.out.println("오늘은 " + today2.name());
System.out.println("오늘은 " + today2);
System.out.println("오늘이 열거객체 순서(0부터 index 시작): " + today2.ordinal());
System.out.println("월요일과 오늘을 비교했을 때 순번 차이(요일 차이 계산) : " + today2.compareTo(Week.금요일));
System.out.println("오늘은(외부입력을 받아 찾을 때 사용) " + Week.valueOf("목요일"));

Week[] days = Week.values(); // days(stack), Week.values -> return array(heap)
for(Week day: days){
System.out.println("Week List : " + day);
}


int max = 0;
int[] array = {1,5,3,8,2};

for(int i=0; i<array.length; i++){
if(max < array[i]) {
max = array[i];
}
}
System.out.println(max);
}

static int add(int scores[]){

int sum = 0;

for(int i=0; i<scores.length; i++){
sum += scores[i];
}

return sum;
}

public enum Week{
월요일,
화요일,
수요일,
목요일,
금요일,
토요일,
일요일
}
}


[예제 코드에서 사용한 표준 라이브러리 클래스]
(1) Calendar
- 컴퓨터의 날짜, 요일, 시간을 사용할 수 있음
- LocalDateTime : java8부터 지원하는 API, 버젼 호환성을 위해 Calendar를 사용함
- 싱글톤 패턴 : 단 하나의 객체만 생성해서 사용, 현재 시스템의 날짜, 시간 정보 + 정보 출력 메서드 등
- static 변수 : 요일, 날짜, 시간을 의미하는 변수
- ctrl + 클래스명 클릭 : 위의 내용을 확인할 수 있음
- API 문서 : http://docs.oracle.com/javase/8/docs/api/ + ctrl + f > Calendar 검색


[생각해본 자바 팁]
(1) main() 메서드 찾기 : 흐름 파악
- 자바 뿐만 아니라 모든 프로그래밍에 있어서 메인 함수를 찾는 것이 중요하다 생각함 



댓글