>  기사  >  Java  >  Java 힙 메모리 및 스택 메모리에 대한 자세한 소개

Java 힙 메모리 및 스택 메모리에 대한 자세한 소개

高洛峰
高洛峰원래의
2017-01-24 14:44:531708검색

Java의 힙과 스택 Java는 메모리를 두 가지 유형으로 나눕니다. 하나는 스택 메모리이고 다른 하나는 힙 메모리입니다.

함수에서 정의한 일부 기본형 변수와 객체 참조 변수는 함수의 스택 메모리에 할당됩니다. 변수가 코드 블록에 정의되면 Java는 스택의 변수에 대한 메모리 공간을 할당합니다. 변수의 범위를 초과하면 Java는 변수에 할당된 메모리 공간을 자동으로 해제하며 메모리 공간은 즉시 해제될 수 있습니다. 다른 용도로 사용하세요.

힙 메모리는 new로 생성된 객체와 배열을 저장하는 데 사용됩니다. 힙에 할당된 메모리는 JVM(Java Virtual Machine) 자동 가비지 수집기에 의해 관리됩니다. 힙에 배열이나 개체가 생성된 후 스택에 특수 변수를 정의할 수도 있습니다. 이 변수의 값은 힙 메모리에 있는 배열이나 개체의 첫 번째 주소와 같습니다. 배열이나 개체의 참조 변수를 얻은 후 스택 메모리의 참조 변수를 사용하여 프로그램의 힙에 있는 배열이나 개체에 액세스할 수 있습니다. 참조 변수는 배열이나 개체의 별칭 또는 코드 이름과 동일합니다. .

참조 변수는 일반 변수이며, 정의되면 메모리가 스택에 할당됩니다. 참조 변수는 프로그램이 실행될 때 범위 외부로 해제됩니다. 배열과 객체 자체는 힙에 할당됩니다. 배열과 객체를 생성하기 위해 new를 사용하는 명령문이 있는 코드 블록 외부에서 프로그램이 실행되더라도 배열과 객체 자체가 차지하는 힙 메모리는 해제되지 않습니다. 배열과 객체는 참조 변수로 지정되지 않으며 사용되면 가비지가 되어 더 이상 사용할 수 없지만 여전히 메모리를 차지하며 나중에 지정되지 않은 시간에 가비지 수집기에 의해 해제됩니다. 이것이 Java가 더 많은 메모리를 차지하는 주된 이유이기도 합니다. 실제로 스택의 변수는 Java의 포인터인 힙 메모리의 변수를 가리킵니다!

Java의 힙과 스택

Java는 메모리를 두 가지 유형으로 나눕니다. 하나는 스택 메모리이고 다른 하나는 힙 메모리입니다.

1. 스택과 힙은 Java에서 Ram에 데이터를 저장하는 데 사용되는 장소입니다. C++와 달리 Java는 스택과 힙을 자동으로 관리하므로 프로그래머가 스택이나 힙을 직접 설정할 수 없습니다.

 2. 스택의 장점은 CPU에 직접 위치한 레지스터 다음으로 액세스 속도가 힙보다 빠르다는 것입니다. 하지만 스택에 저장되는 데이터의 크기와 수명을 결정해야 하고 유연성이 부족하다는 점이 단점이다. 또한 스택 데이터를 공유할 수 있습니다. 힙의 장점은 메모리 크기를 동적으로 할당할 수 있고 수명을 미리 컴파일러에 알릴 필요가 없다는 것입니다. Java의 가비지 수집기는 더 이상 사용되지 않는 데이터를 자동으로 수집합니다. 하지만 런타임에 동적으로 메모리를 할당해야 하기 때문에 접근 속도가 느린 것이 단점이다. ​

  3. Java에는 두 가지 유형의 데이터 유형이 있습니다.

하나는 기본 유형(기본 유형)이며 총 8가지 유형, 즉 int, short, long, byte, float, double, boolean, char이 있습니다(
에는 기본 유형이 없음에 유의하세요). 문자열 유형). 이러한 유형의 정의는 int a = 3; long b = 255L; 과 같은 형식으로 정의되며 자동 변수라고 합니다. 자동 변수는 클래스의 인스턴스가 아닌 리터럴 값을 저장한다는 점에 주목할 가치가 있습니다. 즉, 여기에는 클래스에 대한 참조가 없습니다. 예를 들어 int a = 3입니다. 여기서 a는 int 유형을 가리키는 참조이고,

은 리터럴 값 3을 가리킵니다. 이러한 리터럴 값의 데이터는 크기와 수명이 알려져 있기 때문에 알 수 있습니다(이러한 리터럴 값은 특정 프로그램 블록에서 고정적으로 정의되며, 프로그램 블록이 종료된 후 필드 값이 사라집니다).

추격중 속도의 이유는 스택에 존재한다.


게다가 스택에는 매우 중요한 특수 기능이 있습니다. 즉, 스택에 저장된 데이터를 공유할 수 있다는 것입니다. 동시에 정의한다고 가정해 보겠습니다.


int a = 3;

int b = 3;

컴파일러는 먼저 int a = 3을 처리합니다. 변수 a in 에 대한 참조를 생성한 다음 리터럴 값이 3인 주소가 있는지 검색합니다. 찾지 못하면 주소를 열어 리터럴 값 3을 저장한 다음 a에게 3의 주소를 가리킨다. 그런 다음 b의 참조 변수를 생성한 후 int b = 3을 처리합니다. 스택에 이미 리터럴 값 3이 있으므로 b는 주소 3을 직접 가리킵니다. 이런 식으로 a와 b가 동시에 3을 가리키는 상황이 발생합니다.


이 리터럴 값의 참조는 클래스 객체의 참조와 다르다는 점에 유의하는 것이 중요합니다. 두 클래스 객체의 참조가 동시에 동일한 객체를 가리킨다고 가정합니다. 하나의 객체 참조 변수가 객체의 내부 상태를 수정하면 다른 객체 참조 변수가 즉시 변경 사항을 반영합니다. 대조적으로, 리터럴 참조의 값을 수정해도 리터럴에 대한 다른 참조의 값도 변경되지 않습니다. 위의 예에서와 같이 a와 b의 값을 정의한 후 a=4로 설정하면 b는 4와 같지 않지만 여전히 3과 같습니다. 컴파일러 내부에서 a=4;를 발견하면 스택에 리터럴 값 4가 있는지 다시 검색합니다. 그렇지 않으면 주소가 이미 존재하는 경우 이를 저장하기 위해 다시 엽니다. , 이 주소를 직접 가리킵니다. 따라서 a 값의 변화는 b 값에 ​​영향을 미치지 않습니다.


다른 하나는 Integer, String, Double 및 해당 기본 데이터 유형을 래핑하는 기타 클래스와 같은 래퍼 클래스 데이터입니다. 이러한 모든 유형의 데이터는 힙에 존재합니다. Java는 new() 문을 사용하여 런타임 시 필요에 따라 동적으로 생성된다는 점을 컴파일러에 명시적으로 알려주므로 유연성이 더 높지만 시간이 더 많이 걸린다는 단점이 있습니다.

JAVA에는 데이터를 저장할 수 있는 6가지 위치가 있습니다.

1. 이는 다른 메모리 영역과 다른 위치, 즉 프로세서 내부에 위치하므로 가장 빠른 메모리 영역입니다. 그러나 레지스터의 수는 극히 제한되어 있으므로 컴파일러는 필요에 따라 레지스터를 할당합니다. 직접 제어할 수 없으며 프로그램에 레지스터가 존재한다는 힌트도 느낄 수 없습니다.

2.스택. 범용 RAM에 위치하지만 "스택 포인터"를 통해 프로세서에서 지원됩니다. 스택 포인터가 아래로 이동하면 새 메모리가 할당되고, 위로 이동하면 해당 메모리가 해제됩니다. 이는 레지스터에 이어 두 번째로 빠르고 효율적인 스토리지 할당 방법입니다. 프로그램을 생성할 때 JAVA 컴파일러는 스택 포인터를 위아래로 이동하기 위해 적절한 코드를 생성해야 하기 때문에 스택에 저장된 모든 데이터의 정확한 크기와 수명을 알아야 합니다. 이 제약 조건은 프로그램의 유연성을 제한하므로 일부 Java 데이터(특히 객체 참조)가 스택에 저장되더라도 Java 객체는 스택에 저장되지 않습니다.

3. 모든 JAVA 객체를 저장하는 데 사용되는 범용 메모리 풀(RAM에도 있음)입니다. 힙이 스택과 다르다는 장점은 컴파일러가 힙에서 할당할 저장 영역의 양을 알 필요가 없으며 저장된 데이터가 힙에 얼마나 오래 유지되는지 알 필요가 없다는 것입니다. 따라서 힙에 스토리지를 할당하는 데 유연성이 뛰어납니다. 객체를 생성해야 하는 경우 간단한 새 코드 한 줄만 작성하면 됩니다. 이 코드 줄이 실행되면 저장소가 자동으로 힙에 할당됩니다. 물론 이러한 유연성은 해당 코드로 지불되어야 합니다. 힙을 사용한 스토리지 할당은 스택을 사용한 스토리지보다 시간이 더 걸립니다.

4. 정적 저장. 여기서 "정적"이란 "고정된 위치에 있음"을 의미합니다. 정적 저장소는 프로그램이 실행되는 동안 존재하는 데이터를 저장합니다. static 키워드를 사용하여 객체의 특정 요소를 정적으로 식별할 수 있지만 JAVA 객체 자체는 정적 저장 공간에 저장되지 않습니다.

5. 상시 보관. 상수 값은 일반적으로 프로그램 코드 내에 직접 저장되므로 절대 변경할 수 없으므로 안전합니다. 임베디드 시스템에서는 상수 자체가 다른 부분과 분리되는 경우가 있으므로 이 경우에는 ROM에 넣도록 선택하면 됩니다

6. 데이터가 프로그램 외부에서 완전히 살아남는 경우 프로그램의 제어 없이도 존재할 수 있으며 프로그램이 실행되지 않는 동안에도 존재할 수 있습니다.
속도 면에서는 다음과 같은 관계가 있습니다.

Register

『위 문단은 "Thinking in Java"에서 발췌했습니다. 🎜>

질문 1:

String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true

질문 2:

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false

질문 3:

String s1 = "ja";
String s2 = "va";
String s3 = "java";
String s4 = s1 + s2;
System.out.println(s3 == s4);//false
System.out.println(s3.equals(s4));//true

함수에 정의된 일부 기본 유형 변수와 객체 참조 변수는 함수의 스택 메모리에 할당됩니다.

코드 블록에 변수가 정의되면 Java는 변수에 대한 메모리 공간을 스택에 할당합니다. 변수의 범위를 초과하면 Java는 변수에 할당된 메모리 공간을 자동으로 해제합니다. 메모리 공간은 다른 목적으로 즉시 재사용될 수 있습니다.

힙 메모리는 new로 생성된 객체와 배열을 저장하는 데 사용됩니다.

힙에 할당된 메모리는 자바 가상 머신의 자동 가비지 컬렉터에 의해 관리됩니다.


힙에 배열이나 객체를 생성한 후 스택에 특수 변수를 정의할 수도 있습니다. 그러면 스택에 있는 이 변수의 값이 배열의 첫 번째 주소와 같거나 힙 메모리의 개체, 스택의 이 변수는 배열 또는 개체의 참조 변수가 됩니다.


참조 변수는 배열이나 개체에 이름을 지정하는 것과 동일합니다. 나중에 스택의 참조 변수를 사용하여 프로그램의 힙에 있는 배열이나 개체에 액세스할 수 있습니다.

구체적으로 말하면 스택과 힙은 Java에서 Ram에 데이터를 저장하는 데 사용되는 장소입니다. C++와 달리 Java는 스택과 힙을 자동으로 관리하므로 프로그래머가 스택이나 힙을 직접 설정할 수 없습니다.

Java의 힙은 클래스(객체가 공간을 할당하는 런타임 데이터 영역입니다. 이러한 객체는 new, newarray, anewarray 및 multianewarray와 같은 명령어를 통해 생성되며 프로그램 코드를 명시적으로 해제할 필요가 없습니다. 힙 가비지 수집을 담당하는 힙의 장점은 메모리 크기를 동적으로 할당할 수 있다는 점이며, 런타임에 동적으로 메모리를 할당하고 Java의 가비지 수집기가 자동으로 메모리를 할당하기 때문에 수명을 미리 컴파일러에 알릴 필요가 없습니다. 사용하지 않는 것들을 모아두세요. 단점은 런타임에 메모리를 동적으로 할당해야 하므로 접근 속도가 느리다는 점입니다.

      栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。  

      栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:  

int a = 3;  
int b = 3;  

编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。  

String是一个特殊的包装类数据。可以用:  

String str = new String("abc");
String str = "abc";

   

两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。  
而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。 

          比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。  

String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true

   

可以看出str1和str2是指向同一个对象的。 

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false

   

用new的方式是生成不同的对象。每一次生成一个。  

   因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。  

   另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。(不一定,因为如果事先没有,那么就会创建,这就是创建对象了,如果原来就有,那就指向那个原来的对象就可以了)!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。 

更多Java 堆内存与栈内存详细介绍相关文章请关注PHP中文网!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.