Java学习手册:堆内存&栈内存&方法区


概述

名称特点
堆内存用来存放运行时创建的对象(一般来说,通过new关键字创建出来的对象都放在堆内存中)
栈内存用来存放基本数据类型(例如int、short、long、byte、float、double、boolean以及char等)和引用变量
方法区用来存放类的信息

JVM为Java程序提供并管理所需要的内存空间,JVM内存分为:“”、“”和“方法区”三个区域。


一、堆内存

堆内存用来存放运行时创建的对象。 一般来说,通过new关键字创建出来的对象都存放在堆内存中

①存储所有new出来的对象。
②成员变量的生命周期:对象被创建时存在,对象被回收时消失。
③垃圾回收器(GC):(1)JVM自带的一个小线程,专用于回收垃圾。(2)所谓垃圾是没有任何引用所指的对象。
④垃圾回收的过程是透明的,不定期检查。(自动回收管理)理论上,System.gc()方法可以建议尽快回收,具体实现策略取决于不同的JVM系统。
⑤内存泄漏:无用的对象没有被及时回收。建议:内存不使用时,及时将引用设置为null。

在C++中,堆内存的管理是由开发人员来负责的,也就是说,开发人员在堆中申请的内存,当不再使用时,必须由开发人员来完成堆内存释放的工作。而在Java语言中,这个内存释放的工作由垃圾回收器来负责执行,开发人员只需要申请所需的堆空间而不需要考虑释放的问题。

使用时会出现的问题:如果堆内存使用不当,会产生内存碎片问题。当回收堆内存时,可能导致一些小块的但不连续的内存存在。当用户申请一块较大的堆内存时,虽然可用的小块内存足够大,本可以满足申请的需求,但由于他们是不连续的,导致申请失败。


二、栈内存

栈内存主要用来存放基本数据类型(例如int、short、long、byte、float、double、boolean以及char等)和引用变量,其内存都分配在栈上,变量出了作用域就会自动释放。栈内存的管理是通过压栈和弹栈操作来完成的,以栈帧为基本单位来管理程序的调用关系,每当有函数调用时,都会通过压栈的方式创建新的栈帧,每当函数调用结束后都会通过弹栈的方式释放栈帧。

①用于存放方法中的局部变量(所用到的)。
②调用方法时为该方法分配一个对应的栈帧,栈帧中包含该方法的参数及局部变量,方法调用结束时,栈帧消失,局部变量同样被清除。
③局部变量生命周期:方法被调用时存在,方法调用结束时消失。

和堆内存相比,栈内存要小的多,栈内存的存取速度仅次于寄存器,栈内存里面的数据可以共享,但是其中数据的大小和生存期必须在运行前确定。

使用时会出现的问题:由于栈内存较小,如果栈内存不慎耗尽,就会产生栈溢出(stack overflow)问题。

注:基本数据类型存放在栈里,包装类栈里存放的是对象的引用,即值的地址,而值存放在堆里。


三、方法区

方法区用于存放类的信息,Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区。

类的各种信息(包括方法)都在方法区存储。


四、堆内存和栈内存的联系与区别

1、堆与栈都是内存中存放数据的地方。

2、每当创建一个对象时,它总是存储在堆空间,而栈存储器包含对它的引用。
(在堆中产生了一个数组对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。<Java中引用的用法>)

3、存储在堆中的对象是全局可访问的,而栈内存不能被其他线程访问。

4、栈内存是短暂的,而堆内存从应用程序执行的开始到结束都存在。从堆和栈的功能以及作用来比较,堆主要用来存放对象的,栈主要是用来执行程序的。相较于堆,栈的存取速度更快,但栈的大小和生存期必须是确定的,因此缺乏一定的灵活性。而堆却可以在运行时动态的分配内存,生存期不用提前告诉编译器,但这也导致了其存取速度的缓慢。

5、当堆内存已满时,Java运行时抛出java.lang.StackOverFlowError;当栈内存已满时,则抛出java.lang.OutOfMemoryError:java Heap Space错误;


五、JVM如何管理内存?分成几部分?分别有什么用途?说出下面代码的内部实现原理。

答案:JVM内存分为“堆”、“栈”、“方法区”三个区域,分别用于存储不同的数据。
(1)堆内存用于存储使用new关键字所创建的对象。
(2)栈内存用于存储程序运行时在方法中声明的所有的局部变量。
(3)方法区用于存放类的信息,Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区。类的各种信息(包括方法)都在方法区存储。

//说出下面代码的内部实现原理
Foo foo = new Foo();
foo.f();

上述代码的内部实现原理:
①Foo类首先被装载到JVM的方法区,其中包括类的信息,包括方法和构造等;
②在栈内存中分配引用变量foo;
③在堆内存中按照Foo类型信息分配实例变量内存空间,然后,将栈中引用foo指向foo对象堆内存的首地址;
④使用引用foo调用方法,根据foo引用的类型Foo调用f方法;


六、成员变量与局部变量在内存中的位置

-成员变量局部变量
类中位置类中方法外在方法定义中或者方法声明上
内存中位置在堆内存在栈内存
生命周期随着对象的创建而存在,随着对象的消失而消失随着方法的调用而存在,随着方法的调用完毕而消失
初始化值有默认值初始化没有默认值初始化,必须定义、赋值,然后才能使用

七、栈空间和堆空间

1、栈空间

(1)栈空间存取数据的效率高
(2)栈中的数据按“先进后出”的方式管理
(3)栈空间存储空间较小,不能存放大量的数据
(4)JVM将基本类型的数据存放在栈空间

2、堆空间

(1)堆空间存取数据的效率最低
(2)数据存放的位置随机分配
(3)堆空间存储数据的空间大,能存放大量的数据

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值