Java中的堆内存和栈内存
Posted new-one
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的堆内存和栈内存相关的知识,希望对你有一定的参考价值。
在Java中,堆内存和栈内存是两种不同的内存分配方式。
堆内存
堆内存用于存放由 new
创建的对象和数组。堆内存的分配由Java虚拟机的自动垃圾回收器来管理,因此程序员可以专注于业务逻辑的实现,无需关注内存回收的细节。Java虚拟机会自动检测哪些对象已经不再被引用,然后将其回收释放内存。堆内存的大小和位置在运行时才能确定,并且可通过参数来调节。对于大多数Java应用程序而言,堆内存占据了Java进程内存的大部分。
栈内存
栈内存用于存放程序中的基本数据类型变量和对象的引用变量,以及方法的调用和返回等数据。与堆内存相比,栈内存的开销较小,因此内存分配和回收速度也要快得多。栈上的内存存储只能在定义该变量所在的作用域内有效。当离开该作用域时,变量会被自动销毁。
方法区
方法区用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区也称为永久代(Permanent Generation),但是在Java 8中,永久代已被移除,取而代之的是元空间(Metaspace)。与堆内存不同,方法区的分配不会受到Java虚拟机垃圾回收器的控制。因此,在使用过程中,需要特别注意方法区的内存使用情况。
其他内存
除了上述三种内存外,还有本地方法栈(Native Method Stack)和程序计数器(Program Counter Register):
- 本地方法栈与Java栈类似,不过是为Java调用本地方法(Native Method)服务。
- 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。每个线程都有一个独立的程序计数器,是线程私有的。
如何合理使用不同的内存
以上面的介绍为基础,我们可以在编写Java程序时,通过选择合适的内存分配方式来优化程序性能和内存使用效率。例如,对于较大的对象或数组,可以将它们放在堆内存中分配,而对于生命周期较短的临时变量,则可以使用栈内存分配。
在Java应用程序的开发中,理解内存的分配和管理是非常重要的。在实际开发中,我们需要根据具体情况选择合适的内存分配方式,以提高程序的性能和效率。
参考文献
java中的堆和栈的概念和区别
在说堆和栈之前,我们先说一下JVM(虚拟机)内存的划分:
Java程序在运行时都要开辟空间,任何软件在运行时都要在内存中开辟空间,Java虚拟机运行时也是要开辟空间的。JVM运行时在内存中开辟一片内存区域,启动时在自己的内存区域中进行更细致的划分,因为虚拟机中每一片内存处理的方式都不同,所以要单独进行管理。
JVM内存的划分有五片:
1. 寄存器;
2. 本地方法区;
3. 方法区;
4. 栈内存;
5. 堆内存。
我们重点来说一下堆和栈:
栈内存:栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。
堆内存:存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。
下面我们通过一个图例详细讲一下堆和栈:
比如主函数里的语句 int [] arr=new int [3];在内存中是怎么被定义的:
主函数先进栈,在栈中定义一个变量arr,接下来为arr赋值,但是右边不是一个具体值,是一个实体。实体创建在堆里,在堆里首先通过new关键字开辟一个空间,内存在存储数据的时候都是通过地址来体现的,地址是一块连续的二进制,然后给这个实体分配一个内存地址。数组都是有一个索引,数组这个实体在堆内存中产生之后每一个空间都会进行默认的初始化(这是堆内存的特点,未初始化的数据是不能用的,但在堆里是可以用的,因为初始化过了,但是在栈里没有),不同的类型初始化的值不一样。所以堆和栈里就创建了变量和实体:
那么堆和栈是怎么联系起来的呢?
我们刚刚说过给堆分配了一个地址,把堆的地址赋给arr,arr就通过地址指向了数组。所以arr想操纵数组时,就通过地址,而不是直接把实体都赋给它。这种我们不再叫他基本数据类型,而叫引用数据类型。称为arr引用了堆内存当中的实体。(可以理解为c或c++的指针,Java成长自c++和c++很像,优化了c++)
如果当int [] arr=null;
arr不做任何指向,null的作用就是取消引用数据类型的指向。
当一个实体,没有引用数据类型指向的时候,它在堆内存中不会被释放,而被当做一个垃圾,在不定时的时间内自动回收,因为Java有一个自动回收机制,(而c++没有,需要程序员手动回收,如果不回收就越堆越多,直到撑满内存溢出,所以Java在内存管理上优于c++)。自动回收机制(程序)自动监测堆里是否有垃圾,如果有,就会自动的做垃圾回收的动作,但是什么时候收不一定。
所以堆与栈的区别很明显:
1.栈内存存储的是局部变量而堆内存存储的是实体;
2.栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
3.栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。
https://blog.csdn.net/pt666/article/details/70876410/
以上是关于Java中的堆内存和栈内存的主要内容,如果未能解决你的问题,请参考以下文章
[Java]Java的堆内存和栈内存解析--举例进行内存分析