Java의 스택 및 힙 메모리


99

내가 이해하는 것처럼 Java에서 스택 메모리는 프리미티브 및 메소드 호출을 보유하고 힙 메모리는 객체를 저장하는 데 사용됩니다.

수업이 있다고 가정하자

class A {
       int a ;
       String b;
       //getters and setters
}
  1. a클래스 의 프리미티브 는 어디에 A저장됩니까?

  2. 왜 힙 메모리가 존재합니까? 스택에 모든 것을 저장할 수없는 이유는 무엇입니까?

  3. 객체가 가비지 수집되면 객체와 관련된 스택이 파괴됩니까?


1
stackoverflow.com/questions/1056444/is-it-on-the-stack-or-heap 이 귀하의 질문에 답변하는 것 같습니다.
S.Lott

@ S.Lott, C가 아닌 Java에 관한 것 외에는 예외
Péter Török

@Peter Török : 동의합니다. 코드 샘플은 Java이지만 Java임을 나타내는 태그는 없습니다. 그리고 일반적인 원칙은 Java뿐만 아니라 C에도 적용되어야합니다. 또한 Stack Overflow에 대한이 질문에 대한 많은 답변이 있습니다.
S.Lott

9
@SteveHaigh :이 사이트에서는 모든 사람이 여기에 속하는지 너무 걱정하고 있습니다 ... 질문이 여기에 속하는지 여부에 대한이 사이트가 실제로 어떤 마음가짐을 가지고 있는지 궁금합니다.
Sam Goldberg

답변:


106

스택과 힙의 기본 차이점은 값의 수명주기입니다.

스택 값은 작성된 함수 범위 내에서만 존재합니다. 반환 된 값은 버려집니다.
그러나 힙 값은 힙에 존재합니다. 그것들은 특정 시점에 만들어지고 다른 시점에서 파괴됩니다 (언어 / 런타임에 따라 GC 또는 수동으로).

이제 Java는 스택에 프리미티브 만 저장합니다. 이렇게하면 스택을 작게 유지하고 개별 스택 프레임을 작게 유지하여 더 많은 중첩 된 호출을 허용합니다.
오브젝트는 힙에 작성되며 참조 (기본) 인 스택 만 스택에서 전달됩니다.

따라서 객체를 만들면 객체가 속한 모든 변수와 함께 힙에 배치되므로 함수 호출이 반환 된 후에도 객체가 지속될 수 있습니다.


2
"그리고 오직 참조들 (원시 프리미티브들)"참조들이 왜 프리미티브들이라고 말하는가? 당신은 명확히 할 수 있습니까?
Geek

5
@Geek : 기본 데이터 유형의 공통 정의가 적용되기 때문에 "기본 프로그래밍 블록으로 프로그래밍 언어에서 제공하는 데이터 유형" . 또한 이 기사의 아래에있는 정식 예제들 사이에 참조가 나열되어 있음을 알 수 있습니다 .
back2dos

4
@Geek : 데이터와 관련하여 참조를 포함한 기본 데이터 유형을 숫자로 볼 수 있습니다. 심지어 char의 숫자이며, 그래서 상호 교환 사용할 수 있습니다. 참조는 32 또는 64 비트 길이의 메모리 주소를 나타내는 숫자 일뿐입니다 (하지만 엉망이 아닌 한 그대로 사용할 수는 없지만 sun.misc.Unsafe).
Sune Rasmussen

3
이 답변의 용어가 잘못되었습니다. Java 언어 사양에 따르면 참조는 프리미티브가 아닙니다. 대답의 요점은 맞습니다. (. 당신이 참조가 "어떤 의미에서"있음을 인수 할 수 있지만 기본은 JLS는 자바에 대한 용어를 정의하여입니다, 그것은 원시 유형은 말한다 boolean, byte, short, char, int, long, floatdouble.)
스티븐 C

4
이 기사 에서 알 수 있듯이 Java 스택에 객체를 저장할 있습니다 (또는 작은 수명이 짧은 객체의 레지스터). JVM은 엄밀히 설명 할 수 있습니다. "현재 Java는 프리미티브 만 스택에 저장합니다."라고 말하는 것은 정확하지 않습니다.

49

프리미티브 필드는 어디에 저장됩니까?

원시 필드는 인스턴스화 된 오브젝트의 일부로서 저장되는 . 이것이 어디에 있는지 생각하는 가장 쉬운 방법은 힙입니다. 그러나 항상 그런 것은 아닙니다. Java 이론 및 실습에 설명 된대로 : 도시 성능 범례는 다음 과 같이 다시 방문했습니다 .

JVM은 이스케이프 분석이라는 기술을 사용하여 특정 객체가 전체 수명 동안 단일 스레드에 국한되어 있고 해당 수명이 주어진 스택 프레임의 수명에 의해 제한되어 있음을 알 수 있습니다. 이러한 객체는 힙 대신 스택에 안전하게 할당 될 수 있습니다. 또한 작은 객체의 경우 JVM이 할당을 완전히 최적화하고 객체의 필드를 레지스터로 호이스트 할 수 있습니다.

따라서 "객체가 만들어지고 필드도 있습니다"라는 말 외에는 무언가가 힙이나 스택에 있는지 말할 수 없습니다. 작고 수명이 짧은 개체의 경우 '개체'가 메모리에 존재하지 않고 대신 필드를 레지스터에 직접 배치 할 수 있습니다.

이 논문은 다음과 같이 끝납니다.

JVM은 놀랍게도 개발자 만 알 수 있다고 생각했던 것을 알아내는 데 능숙합니다. JVM이 사례별로 스택 할당과 힙 할당을 선택할 수 있도록함으로써 프로그래머가 스택에 할당할지 힙에 할당할지 고민하지 않고도 스택 할당의 성능 이점을 얻을 수 있습니다.

따라서 다음과 같은 코드가있는 경우 :

void foo(int arg) {
    Bar qux = new Bar(arg);
    ...
}

(가) 어디에서 ...허용하지 않는 qux그 범위를 떠나, qux 대신 스택에 할당 될 수있다. 가비지 수집이 필요하지 않기 때문에 실제로 VM에서 승리합니다. 범위를 벗어나면 사라집니다.

Wikipedia의 이탈 분석 에 대한 추가 정보 . IBM의 Escape Analysis for Java에 대한 논문을 기꺼이 파고 드는 사람들을 위해 . C # 세계에서 온 사람들에게는 스택이 구현 세부 사항 이며 Eric Lippert의 가치 유형대한 진실을 읽을 수 있습니다. . .Net 서적이 스택 대 힙 메모리 할당에 대해 이야기하는 이유는 무엇입니까? 이것도 들어갑니다.

스택과 힙의 이유

힙에

그렇다면 왜 스택이나 힙이 있습니까? 범위를 벗어나는 물건의 경우 스택이 비쌀 수 있습니다. 코드를 고려하십시오 :

void foo(String arg) {
    bar(arg);
    ...
}

void bar(String arg) {
    qux(arg);
    ...
}

void qux(String arg) {
    ...
}

매개 변수도 스택의 일부입니다. 힙이없는 경우 스택의 전체 값 세트를 전달합니다. 이것은 "foo"작은 문자열에는 적합하지만 누군가가 그 문자열에 큰 XML 파일을 넣으면 어떻게 될까요? 각 호출 스택에 전체 큰 문자열을 복사합니다 - 그리고 그것은 매우 낭비입니다.

대신, 생명체가 다른 범위 (다른 범위로 전달되고 다른 사람이 유지 관리중인 구조 등)에있는 것을 힙이라고하는 다른 영역에 배치하는 것이 좋습니다.

스택에

스택이 필요 하지 않습니다 . 가설 적으로, (임의의 깊이로) 스택을 사용하지 않는 언어를 작성할 수 있습니다. 내가 젊었을 때 배운 오래된 BASIC은 그렇게 했으므로 8 레벨의 gosub호출 만 할 수 있었고 모든 변수는 전역 적이었습니다. 스택은 없었습니다.

스택의 장점은 범위에 존재하는 변수가있을 때 해당 범위를 벗어나면 해당 스택 프레임이 팝된다는 것입니다. 실제로 존재하는 것과 존재하지 않는 것을 단순화합니다. 프로그램은 다른 절차 인 새 스택 프레임으로 이동합니다. 프로그램이 프로 시저로 돌아가고 현재 범위를 확인하는 절차로 돌아갑니다. 프로그램이 프로 시저를 떠나 스택의 모든 항목이 할당 해제됩니다.

이것은 실제로 코드를 위해 런타임을 작성하는 사람이 스택과 힙을 사용하는 것을 쉽게 만듭니다. 단순히 코드로 작업하는 많은 개념과 방법으로 언어로 코드를 작성하는 사람이 코드를 명확하게 생각할 수 없습니다.

스택의 특성은 또한 조각화 될 수 없음을 의미합니다. 메모리 조각화 는 힙의 실제 문제입니다. 몇 개의 객체를 할당 한 다음 가비지가 중간 객체를 수집 한 후 다음으로 큰 객체를 할당 할 공간을 찾으십시오. 엉망입니다. 대신 스택에 물건을 넣을 수 있다는 것은 그 일을 처리 할 필요가 없다는 것을 의미합니다.

가비지 수집 된 경우

무언가가 가비지 수집되면 사라집니다. 그러나 이미 잊어 버렸기 때문에 가비지 수집 만됩니다. 프로그램의 현재 상태에서 액세스 할 수있는 프로그램의 객체에 대한 참조가 더 이상 없습니다.

이것이 가비지 수집을 매우 단순화 한 것임을 지적하겠습니다. 많은 가비지 수집기가 있습니다 (Java 내부에서도-다양한 플래그 ( docs ) 를 사용하여 가비지 수집기를 조정할 수 있습니다 . 이들은 다르게 동작하며 각 작업이 수행되는 방식의 뉘앙스는이 답변에 대해 너무 깊습니다. Java Garbage Collection Basics 는 그 중 일부가 어떻게 작동하는지 더 잘 알 수 있습니다.

즉, 스택에 무언가가 할당되면 일부로 가비지 수집되지 않습니다 System.gc(). 스택 프레임이 튀어 나올 때 할당이 해제됩니다. 무언가가 힙에 있고 스택의 무언가에서 참조 된 경우 해당 시점에 가비지 수집되지 않습니다.

이것이 왜 중요한가?

대부분의 전통. 작성된 교재와 컴파일러 클래스 및 다양한 비트 문서는 힙과 스택에 대해 많은 부분을 차지합니다.

그러나 오늘날의 가상 머신 (JVM 및 이와 유사한)은 프로그래머에게이를 숨기려고 많은 노력을 기울였습니다. 스토리지 공간을 적절하게 늘리기보다는 그 이유 를 알아야 할 필요가 없다면 , 그다지 중요하지 않습니다.

개체가 어딘가에 있고 개체가 존재 하는 적절한 시간 동안 정확하고 빠르게 액세스 할 수있는 위치에 있습니다. 스택이나 힙에 있으면 실제로 중요하지 않습니다.


7
  1. 힙의 경우 스택의 포인터가 참조하는 객체의 일부입니다. 즉. a와 b는 서로 인접하여 저장됩니다.
  2. 모든 메모리가 스택 메모리라면 더 이상 효율적이지 않을 것입니다. 시작하기에 작고 빠른 액세스 영역이 있고 훨씬 더 큰 메모리 영역에 해당 참조 항목이있는 것이 좋습니다. 그러나 이것은 객체가 단순히 하나의 프리미티브 인 경우 스택의 포인터와 거의 동일한 공간을 차지할 때 과잉입니다.
  3. 예.

1
포인트 # 2에 스택에 객체를 저장하면 (수십만 개의 항목이있는 사전 객체를 상상하면) 객체를 전달하거나 함수에서 객체를 반환하려면 객체를 복사해야합니다. 시각. 힙의 객체에 대한 포인터 또는 참조를 사용하면 (작은) 참조 만 전달합니다.
Scott Whitlock

1
객체가 가비지 수집되고 있다면 그것을 가리키는 스택에 참조가 없기 때문에 3은 '아니오'라고 생각했습니다.
Luciano

@Luciano-당신의 요점을 참조하십시오. 질문 3을 다르게 읽습니다. "동시"또는 "당시"는 암시 적입니다. :: 어깨를 으 :: ::
pdr

3
  1. Java가 이스케이프 분석을 통해 시맨틱에 영향을 미치지 않음을 입증 한 후 스택에서 클래스 인스턴스를 최적화로 할당하지 않는 한 힙에서. 그러나 이것은 세부적인 구현이므로, 미세 최적화를 제외한 모든 실제적인 목적에 대한 답은 "힙에 있습니다".

  2. 스택 메모리는 처음부터 순서대로 할당 및 할당 해제해야합니다. 힙 메모리는 임의의 순서로 할당 및 할당 해제 할 수 있습니다.

  3. 객체가 가비지 수집되면 스택에서 객체를 가리키는 참조가 더 이상 없습니다. 있다면, 그들은 객체를 살아있게 유지할 것입니다. 스택 프리미티브는 함수가 반환 될 때 자동으로 파괴되므로 가비지 수집되지 않습니다.


3

스택 메모리는 로컬 변수 및 함수 호출을 저장하는 데 사용됩니다.

힙 메모리는 Java로 오브젝트를 저장하는 데 사용됩니다. 상관없이 코드에서 객체가 생성되는 위치

프리미티브 a는 어디에 class A저장됩니까?

이 경우 기본 요소 a는 클래스 A 오브젝트와 연관됩니다. 따라서 힙 메모리에 작성됩니다.

왜 힙 메모리가 존재합니까? 스택에 모든 것을 저장할 수없는 이유는 무엇입니까?

  • 스택에서 생성 된 변수는 범위를 벗어나 자동으로 파괴됩니다.
  • 스택은 힙의 변수에 비해 할당 속도가 훨씬 빠릅니다.
  • 힙의 변수는 가비지 콜렉터에서 제거해야합니다.
  • 스택의 변수에 비해 할당 속도가 느립니다.
  • 컴파일 시간 전에 할당 해야하는 데이터의 양을 정확히 알고 너무 크지 않은 경우 스택을 사용합니다. (기본 로컬 변수는 스택에 저장 됨)
  • 런타임시 필요한 데이터 양을 정확히 모르거나 많은 데이터를 할당해야하는 경우 힙을 사용합니다.

객체가 가비지 수집되면 객체와 관련된 스택이 파괴됩니까?

가비지 콜렉터는 힙 메모리 범위에서 작동하므로 참조 체인이없는 오브젝트를 루트로 파괴합니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.