[java] 8. 자바의 메모리관리


JVM의 메모리구조



자바는 운영체제에 제한을 받지 않는다고 하였는데요~

이는 Java Virtual Machine (JVM,자바가상머신) 때문입니다.

이번 시간에서는 JVM의 메모리 구조를 알아보겠습니다.

응용프로그램이 실행되면, JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당받고

JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리합니다.

3가지 주요영역이 있는데 바로 method area, call stack, heap 입니다.


image




<메서드 영역>


프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일을 읽어서 분석하여


클래스에 대한 정보(클래스 데이터)를 이곳에 저장한다.


이 때, 그 클래스의 클래스변수도 이 영역에 함께 생성된다.




<힙 영역>


인스턴스가 생성되는 공간. 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다.


즉, 인스턴스 변수들이 생성되는 공간이다.




<호출 스택>


호출 스택은 메서드의 작업에 필요한 메모리 공간을 제공한다.


메서드가 호출되면, 호출스택에 호출된 메서드를 위한 메모리가 할당되며,


이 메모리는 메서드가 작업을 수행하는 동안 지역변수들과 연산의 중간결과 등을 저장하는데 사용된다.


그리고 메서드가 작업을 마치면 할당되었던 메모리공간은 반환되어 비워진다.


각 메서드를 위한 메모리 상의 작업공간은 서로 구별되며,


첫 번째로 호출된 메서드를 위한 작업공간이 호출스택의 맨 밑에 마련되고, 


첫 번째 메서드 수행 중에 다른 메서드를 호출하면,


첫 번째 메서드의 바로 위에 두 번째로 호출된 메서드를 위한 공간이 마련된다.


이 때 첫 번째 메서드는 수행을 멈추고, 두 두번째 메서드가 수행되고


두 번째로 호출된 메서드가 수행을 마치게 되면, 두 번째 메서드를 위해 제공되었던 호출스택의 메모리공간이 반환되며,


첫 번째 메서드는 다시 수행을 계속하게 된다.



다음은 call stack에 대한 예제입니다.


image



기본형 매개변수와 참조형 매개변수



자바에서는 메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨줍니다.

매개변수의 타입이 기본형일 때는 기본형 값이 복사되겠지만,

참조형이면 인스턴스의 주소가 복사됩니다.


기본형 매개변수: 변수의 값을 읽기만 할 수 있다.(read only)

참조형 매개변수: 변수의 값을 읽고 변경할 수 있다(read & write)



다음은 기본형 매개변수와 참조형 매개변수의 차이점을 보여주는 예제입니다.


image



위의 예제에서 봤듯이 매개변수가 기본형인 경우에는 값만 전달되기에 메서드 내에서 메서드 밖에 있는 변수를 변경할 수 없습니다.

하지만, 참조형 매개변수는 주소를 전달하기에 메서드 내에서 메서드 밖의 변수를 변경할 수 있습니다.

참조형은 객체 뿐만아니라 배열도 해당됩니다.


image



참조형 반환타입



매개변수뿐만 아니라 반환타입도 참조형이 될 수 있습니다.

반환타입이 참조형이라는 것은 반환하는 값의 타입이 참조형이라는 얘긴데,

모든 참조형 타입의 값은 객체의 주소이므로 그저 정수값이 반환되는 것일 뿐 특별할 것이 없습니다.

다음은 반환타입이 참조형인 경우의 예제입니다.


image



재귀호출



매서드의 내부에서 메서드 자신을 다시 호출하는 것을 재귀호출(recursive call)이라 하고,

재귀 호출을 하는 메서드를 재귀 메서드라고 합니다.


void method(){
	method(); //재귀호출. 메서드 자신을 호출한다.
}




하지만, 위의 코드처럼 오로지 재귀호출뿐이면, 무한히 자기 자신을 호출하기 때문에 무한반복에 빠지게 됩니다.

따라서 재귀호출은 조건문이 필수적으로 따라다닙니다.

다음은 재귀호출의 대표적인 예제 피보나치 수열입니다.


image



클래스 메서드(static메서드)와 인스턴스 메서드



변수에서 그랬던 것과 같이, 메서드 앞에 static이 붙어 있으면 클래스 메서드이고 붙어 있지 않으면 인스턴스 메서드입니다.

클래스 메서드도 클래스 변수처럼, 객체를 생성하지 않고도 ‘클래스이름.메서드이름’ 형식으로 호출이 가능하고,

반면에, 인스턴스 메서드는 반드시 객체를 생성해야만 호출할 수 있습니다.


1.클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통으로 사용하는 것에 staitc을 붙인다.


2.클래스 변수(static변수)는 인스턴스를 생성하지 않아도 사용할 수 있다.


3.클래스 메서드(static 메서드)는 인스턴스 변수(멤버 변수)를 사용할 수 없다.


반면에 인스턴스 변수나 인스턴스 메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능하다.


4.메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.



다음은 클래스 메서드와 인스턴스 메서드와 관련된 예제입니다.


image



클래스 멤버와 인스턴스 멤버간의 참조와 호출



같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능합니다.

단, 클래스멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야합니다.

왜냐하면, 인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만,

클래스 멤버가 존재하는 시점에 인스턴스 멤버가 존재하지 않을 수도 있기 때문입니다.


class TestClass{
	void instanceMethod(){} //인스턴스 메서드
	static void staticMethod(){} //static 메서드
	
	
	void instanceMethod2(){ //인스턴스 메서드
		instanceMethod(); //다른 인스턴스 메서드를 호출
		staticMethod(); //static 메서드를 호출
	
	
	static void staticMethod2(){ //static 메서드
		instanceMethod(); //에러! 인스턴스 메서드를 호출할 수 없다.
		staticMethod; //가능
	}
}



메서드간의 호출과 마찬가지로 인스턴스메서드에서는 인스턴스 변수를 사용할 수 있지만,

static 메서드는 인스턴스 변수를 사용할 수 없습니다.


class TestClass2{
	int iv; //인스턴스 변수
	static int cv; //클래스 변수
	
	
	void instanceMethod(){ //인스턴스 메서드
		System.out.println(iv); //인스턴스 변수 사용 가능
		System.out.println(cv); //클래스 변수 사용 가능
	}
	
	
	static void staticMethod(){ //static 메서드
		System.out.println(iv); //에러! 인스턴스 변수를 사용할 수 없다.
		System.out.println(cv); //클래스 변수는 사용할 수 있다.
	}
}



그리고 인스턴스 생성과 동시에 메서드를 호출하는 것도 가능합니다.


new TestClass2().instanceMethod(); //가능



하지만 참조 변수를 선언하지 않았기 때문에 생성된 TestClass2 인스턴스는

메서드 호출 후 더이상 사용하지 못합니다.