>  기사  >  Java  >  Java에서 힙, 스택 및 스택의 차이점은 무엇입니까? Java의 힙, 스택 및 스택의 차이점 소개

Java에서 힙, 스택 및 스택의 차이점은 무엇입니까? Java의 힙, 스택 및 스택의 차이점 소개

不言
不言앞으로
2018-11-20 15:54:252560검색

이 기사에서는 Java에서 힙, 스택 및 스택의 차이점이 무엇인지 설명합니다. Java의 힙, 스택 및 스택의 차이점에 대한 소개에는 특정 참조 값이 있습니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

공식 콘텐츠가 시작되기 전에 한 가지 설명하겠습니다. 우리가 흔히 스택이라고 부르는 것은 힙(Heap)이고 스택은 스택(Stack)이라고 통칭됩니다. 1. 스택)과 힙은 Java에서 Ram에 데이터를 저장하는 데 사용되는 장소입니다. C++와 달리 Java는 스택과 힙을 자동으로 관리하므로 프로그래머가 스택이나 힙을 직접 설정할 수 없습니다.


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


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


하나는 기본 유형으로 int, short, long, byte, float, double, boolean, char 총 8가지 유형이 있습니다(기본 문자열 유형이 없음에 유의하세요). 이러한 유형의 정의는 int a = 3; long b = 255L; 과 같은 형식으로 정의되며 자동 변수라고 합니다. 자동 변수는 클래스의 인스턴스가 아닌 리터럴 값을 저장한다는 점에 주목할 가치가 있습니다. 즉, 여기에는 클래스에 대한 참조가 없습니다. 예를 들어 int a = 3입니다. 여기서 a는 리터럴 값 3을 가리키는 int 유형을 가리키는 참조입니다. 이러한 리터럴 값의 데이터는 크기와 수명을 알 수 있으므로(이러한 리터럴 값은 특정 프로그램 블록에서 고정적으로 정의되며, 프로그램 블록이 종료되면 필드 값은 사라집니다), 속도를 위해 , 그들은 스택에 존재합니다.


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

 int a = 3;
 int b = 3;

컴파일러는 먼저 int a = 3을 처리합니다. 먼저 스택에서 변수 a에 대한 참조를 만든 다음 리터럴 값 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() 문을 사용하여 런타임 시 필요에 따라 동적으로 생성된다는 점을 컴파일러에 명시적으로 알려주므로 유연성이 더 높지만 시간이 더 많이 걸린다는 단점이 있습니다.

4. String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建,也可以用String str = "abc";的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK 5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java 中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单 例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = "abc";中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。

5. 关于String str = "abc"的内部工作。Java内部将此语句转化为以下几个步骤:

(1)先定义一个名为str的对String类的对象引用变量:String str;

(2)在栈中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着创建一个新的String类的对象o,并 将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址,则查找对象o,并返回o的地址。

(3)将str指向对象o的地址。

值得注意的是,一般String类中字符串值都是直接存值的。但像String str = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!

为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。

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

 注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。 
结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。

我们再来更进一步,将以上代码改成:

 String str1 = "abc"; 
 String str2 = "abc"; 
 str1 = "bcd"; 
 System.out.println(str1 + "," + str2); //bcd, abc 
 System.out.println(str1==str2); //false

 这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。

事实上,String类被设计成为不可改变(immutable)的类。如果你要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然 后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的,但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中,会带有一定的不良 影响。

再修改原来代码:

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

str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用 str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。

我们再接着看以下的代码。

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

创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。

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

创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。 

以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。

6. 数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。

7. 结论与建议:

(1)我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向 String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因 此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认 识到这一点对排除程序中难以发现的bug是很有帮助的。

(2)使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是 享元模式的思想,但JDK的内部在这里实现是否应用了这个模式,不得而知。

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

(4)由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

栈(stack):是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量. 
在java中,所有基本类型和引用类型都在栈中存储.栈中数据的生存空间一般在当前scopes内(就是由{...}括起来的区域).

堆(heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),C中的malloc语句所产生的内存空间就在堆中.

Java에서는 new xxx()를 사용하여 생성된 모든 개체가 힙에 저장됩니다. 가비지 수집기가 개체가 참조되지 않음을 감지하면 자동으로 개체를 삭제합니다. Java에서는 객체를 가리키는 참조 유형만 있으면 어디에서나 사용할 수 있다고 합니다. # 🎜🎜#JAVA에는 데이터를 저장할 수 있는 6가지 위치가 있습니다.

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

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

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

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

5. 지속적인 보관. 상수 값은 일반적으로 프로그램 코드 내에 직접 저장되므로 절대 변경할 수 없으므로 안전합니다. 가끔 임베디드 시스템에서는 상수 자체가 다른 부분과 분리되어 있는 경우가 있는데, 이 경우에는 ROM

#🎜 🎜##🎜에 넣도록 선택하면 됩니다. 🎜#6. 비 RAM 저장. 데이터가 프로그램 외부에서 완전히 살아남는 경우 해당 데이터는 프로그램의 제어에서 자유롭고 프로그램이 실행되지 않는 동안에도 존재할 수 있습니다. 속도 측면에서는 다음과 같은 관계가 있습니다.

#🎜🎜 #스택등록 #

여기에서는 주로 힙과 스택의 관계에 대해 이야기합니다.

힙: 힙은 소위 동적 메모리인 힙입니다. 그 안에 있는 메모리는 재활용 및 할당이 가능합니다. 필요하지 않을 때 새 메모리에 요청하면 메모리의 데이터는 순서가 없습니다. 즉, 먼저 할당된 메모리와 나중에 할당되는 메모리 사이에 필요한 위치 관계가 없으며 해제할 때 순서가 없습니다. 일반적으로 Malloc은 힙을 할당하고 수동으로 해제해야 합니다.

스택: STACK입니다. 실제로는 입구와 출구가 하나만 있는 대기열, 즉 First In Last Out(First In Last Out) 먼저 할당된 메모리를 나중에 해제해야 합니다. 일반적으로 함수 매개변수 값, 로컬 변수 등을 저장하기 위해 시스템에 의해 자동으로 할당되며, 자동으로 지워집니다.

또한 힙은 전역적이며 스택은 각 함수가 들어갈 때 작은 조각으로 나누어지고 함수가 반환될 때 해제되며 정적 및 전역 변수와 new로 얻은 변수는 모두 힙에 배치됩니다. heap., 지역 변수는 스택에 배치되므로 함수가 반환되면 모든 지역 변수가 사라집니다.

실제로 실제 애플리케이션에서 스택은 대부분 메서드 호출을 저장하는 데 사용됩니다. 힙은 객체 저장에 사용됩니다.

JAVA의 기본 유형에는 실제로 특별한 처리가 필요합니다. JAVA에서는 new를 통해 생성된 객체가 "힙"에 저장되기 때문에 new를 사용하여 기본 유형 등과 같은 작고 간단한 변수를 생성하는 것은 그다지 효율적이지 않은 경우가 많습니다. 따라서 JAVA에서는 이러한 유형에 대해 C 및 C++와 동일한 방법이 사용됩니다. 즉, 생성을 위해 new를 사용하는 대신 "참조"가 아닌 "자동" 변수를 생성합니다. 이 변수는 해당 "값"을 소유하고 스택에 배치되므로 더욱 효율적입니다.

위 내용은 Java에서 힙, 스택 및 스택의 차이점은 무엇입니까? Java의 힙, 스택 및 스택의 차이점 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 cnblogs.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제