浅析Java内存区域

栏目: Java · 发布时间: 8年前

内容简介:浅析Java内存区域

内存管理是开发者必须掌握的基本功,不然程序总是会在各种难以捉摸的错误中崩溃,一些语言,例如C、C++开发者们自己申请内存,使用完自己释放,但是不当的代码书写习惯往往导致内存泄露,引用空指针等等错误,而 Java 借助于虚拟机帮我们完成了许多工作,使开发者从内存管理的深坑中爬出来了,但是由于隔着这层虚拟机,出现问题时的应对策略更显功力,需要对虚拟机内存管理机制的深入了解。

这篇文章只是粗浅的介绍下虚拟机内存区域的大概分布,让初学者在脑海中有个大概印象,而印象的开始则借助于下面的一幅图:

浅析Java内存区域

Java内存区域分为大的两个区域,一部分是线程共享的,另一部分则是每个线程所独有的。刚开始了解编程时,大致就有印象,栈内存存在于方法体中,定义的那些变量什么的都是栈内存,方法结束就没了,堆内存则是使用new关键字申请出来的。当然这只是粗线的认识,下面一块块的说上图中的内存分布。

程序计数器

类似于CPU中的PC寄存器,用于存放下一条指令的地址,但是虚拟机不使用CPU的程序计数器,而是自己在内存里设立一片区域模拟CPU的程序计数器。改变计数器的值来选取下一条需要执行的字节码指令,包括分支、循环、跳转、异常、线程恢复等基础功能都依赖于计数器。

Java的每个线程都有其独立的计数器,计数器之间互不影响,这样当多线程操作时,一个挂起的线程在恢复时,仍然能够从计数器中恢复之前运行到的地方,继续执行。所以说程序计数器也是线程隔离的。执行Java方法时,计数器中存放的是虚拟机字节码的地址,而运行Native方法时则是空(undefined)。该区域不会产生OutOfMemoryError。

虚拟机栈

Java虚拟机栈也是线程私有的,它的生命周期等同于线程的生命周期。虚拟机栈描述的是Java方法执行时的内存模型。当方法执行时,会创建一个栈帧,用于存储方法执行期间所用到的数据结构,包含 局部变量表,操作数栈,动态链接,方法出口 等信息。

当一个方法执行时,一个包含以上元素的栈帧入栈,当方法退出时,栈帧出栈。一般我们都会知道内存区分为堆区和栈区,实际上也是个粗浅的分法,栈指的就是虚拟机栈,而其中最重要的部分就是局部变量表。

Java的垃圾收集器是不会去回收栈上的内容的,因为栈上的内容总是随着方法的结束自动释放。局部变量表包含着各种编译期已知的 基本数据类型对象引用returnAddress 。基本数据类型就是Java的8大基本数据类型(boolean,byte,char, short, int, float, long, double),对象引用,你可以把它当成指向实际对象地址的指针或者一个代表对象的句柄,returnAddress则是一条字节码指令的地址。当进入一个方法时,它所需要分配的空间在编译期就是已知的了。

在虚拟机栈中可能会报以下两种异常:

  • StackOverflowError: 线程请求的栈深度大于所允许的深度
  • OutOfMemoryError: 大多数虚拟机栈是可以动态扩展的,如果无法申请到足够内存,就会抛出。

本地方法栈

区别于虚拟机栈执行的是Java方法,本地方法栈则是虚拟机使用的Native方法服务,我们在看一些库的源码时正常定位到最后就是用Native方法实现的,但是在虚拟机规范里对本地方法使用的语言,使用方式进行硬性规定,所以虚拟机可以任意实现它。HotSpot中本地方法栈和虚拟机栈是合在一起的。

至少从学习 C语言 时,我们就听说malloc方法会在堆上分配空间。Java的堆上也是分配实例对象的。虚拟机规范上讲,基本所有的对象实例和数组都分配的堆上,例如上面栈上对象引用指向的对象,都是在堆上分配的。但是随着编译器技术的发展,所有对象都在堆上分配就不是那么纯粹了。

堆也是垃圾收集(GC)的主阵地。现代收集器基本都采用了分代收集算法,所以堆也可以划分为新生代和老年代,再细致点还有Eden空间,From Survivor空间和To Survivor空间。由于虚拟机实现了自动垃圾收集,所以在Java中,堆中new出的对象是不需要手动释放的。

我们可以通过 -Xmx-Xms 控制堆的默认大小,如果在堆中再也申请不到内存,则会抛出 OutOfMemoryError 异常。

方法区

方法区也是各个线程间共享的区域,一般存储已被加载的 类信息、常量、静态变量、即时编译器编译后的代码 等数据。一般很多人也把方法区称为永久代。其实仅仅是HotSpot团队把GC分代收集也扩展到方法区了,让垃圾收集器可以一块回收方法区的内存,但是其它的虚拟机实现没有这么做,也不存在永久代的,HotSpot自身也已经在JDK1.7上移除了永久代中的常量池。

相对而言,垃圾收集在方法区是不怎么出现的。这个区域主要回收的是常量池和类型的卸载,但是实际上对二者的回收发生的条件极为苛刻,很少发生收集。

运行时常量池是方法区的一部分,Class文件中包含常量池信息,用于存放编译期生成的各种字面量和符号引用,这部分在类加载后进入方法区的运行时常量池。这部分可以参考我的: 深入理解JVM类文件格式


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Modeling the Internet and the Web

Modeling the Internet and the Web

Pierre Baldi、Paolo Frasconi、Padhraic Smyth / Wiley / 2003-7-7 / USD 115.00

Modeling the Internet and the Web covers the most important aspects of modeling the Web using a modern mathematical and probabilistic treatment. It focuses on the information and application layers, a......一起来看看 《Modeling the Internet and the Web》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具