《深入理解Java虚拟机》笔记1

1.虚拟机运行时数据区域:

这里写图片描述

程序计数器(Program Counter Register):
是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器(线程私有)。
由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内
核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条
线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

Java虚拟机栈(Java Virtual Machine Stacks):
也是线程私有的,它的生命周期与线程相同。
虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口
等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
平时所指的“栈”就是现在讲的虚拟机栈,或者说是虚拟机栈中的局部变量表部分。
其中64位长度的long和double类型的数据会占用2个局部变量空间

局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

本地方法栈(Native Method Stacks):
与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。
本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

Java堆(Java Heap)
对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域。
此内存区域的唯一目的就是存放对象实例(所有的对象实例以及数组都要在堆上分配)
Java堆是垃圾收集器管理的主要区域
从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法
从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区,进一步划分的目的是为了更好地回收内存,或者更快地分配内存。
如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
另外,在Java堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存储在方法区中。

方法区(Method Area)
与Java堆一样,是各个线程共享的内存区域。
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。
根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

方法区中 对象类型数据 中有一个指向该类方法的方法表。

扫描二维码关注公众号,回复: 10749109 查看本文章

运行时常量池(Runtime Constant Pool)
是方法区的一部分,但会专门区分开。
既然运行时常量池是方法区的一部分,自然会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

2.垃圾收集算法:
1.标记-清除算法:
最基础的回收算法,先标记要回收的对象,然后统一回收已标记的对象。
不足:效率低,会产生大量内存碎片。

2.复制算法:
将内存分为两块,每次只使用其中一块内存。当其中一块使用完,将还要存活的对象复制到另一块区域,然后清理掉前者的所有对象。
优点:有效避免内存碎片
不足:需要多余的内存空间
一般用于对象存活率较低的情形。

现在的商业虚拟机都采用这种收集算法回收新生代。
将内存划分为较大的Eden空间和两块较小的Survivor空间(大概是8:1),每次使用Eden和一块Survivor,回收时将Eden和该Survivor中还存活的对象一次性复制到
到另外一块Survivor,最后清理掉Eden和原Survivor中的对象。如果另外一块Survivor空间不够,则对象直接进入老年代(额外的分配担保)。

3.标记-整理算法:
先标记存活的对象,然后存活的对象向一端移动,最后清理端边界的对象。
一般老年代使用这种算法,因为老年代对象存活率高,如果使用复制算法效率低,且分配担保需要很大空间。

发布了69 篇原创文章 · 获赞 76 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/sinat_23092639/article/details/77930229
今日推荐