深入理解Java虚拟机——第二章笔记与总结

本书的第二章的标题是:Java内存区域与内存溢出异常。

首先上一幅图来表明JVM运行时的内存区域划分。

Java虚拟机运行时数据区

1.程序计数器(Program Counter Register)是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。我个人觉得这个就相当于CPU里的eip寄存器,专门用来存储下一条要执行的指令的地址 。当然,根据它的作用我们就可以知道这个程序计数器是每个线程私有的。

2.如果线程正在执行Java方法,那么程序计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是Native方法,也就是本地方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

3.Java虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈动态链接、方法出口等信息。每个Java方法从调用到执行完成的过程,就对应着入栈到出栈的过程。(其中的操作数栈和动态链接是什么东西啊???)

4.局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型。

5.对于Java虚拟机栈的有两种异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

6.本地方法栈的作用与Java虚拟机栈的作用非常类似。只不过本地方法栈是专门为虚拟机使用的本地方法服务的。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

7.Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例。概括而言,Java堆会划分为新生代和老年代。而至于Java堆内部是如何划分的是第三章的主题。

8.Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。如果在堆中没有内存完成实例分配,并且也无法扩展,将会抛出OutOfMemoryError异常。

9.方法区(Method Area)也是各个线程共享的内存区域。记住,方法区不像它的名字那样,只会存储方法。而是负责存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

10.很多人习惯上把方法区称为永久代,但是本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择使用永久代来实现方法区,从而可以使用Java堆的垃圾收集器来管理这部分内存。在JDK1.7的HotSpot中,已经把原本放在永久代的字符串常量池移出。

11.方法区的内存回收目标主要是针对常量池的回收和对类型的卸载。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

12.运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类被加载后存储在运行时常量池。当常量池无法申请到内存时会抛出OutOfMemoryError异常。

13.在虚拟机运行时数据区之外还有一个内存区域,被称为直接内存(Direct Memory)。这部分内存区域也会抛出OutOfMemoryError异常。讲真这部分内存区域我是实在不能理解(好像是NIO要使用到它),暂且先在这里记下,日后再慢慢参透。

14.对于对象的创建,空间的分配有两种方式,分为:指针碰撞和空闲列表。具体使用哪种分配方式,由Java堆使用的垃圾收集算法决定 。还有,空间的分配还要考虑线程安全问题。对此有两种方案,一种是对所有分配内存空间的动作都进行同步处理。另一种是为每个线程先分配一小块内存称为本地线程分配缓冲(TLAB),这样就不用每次分配空间都同步,而变为只在分配TLAB的时候需要同步操作。

15.对象的空间分配完之后,就要把空间初始化为零值,而对于对象头则要设置正确的信息。然后,<init>方法开始执行。

16.对象的内存布局分为对象头、实例数据和对齐填充(Padding)等三部分。其中,对象头有运行时数据和类型指针,而数组对象还有数组的长度。其实,有些实现是没有类型指针的,这具体要看对象如何访问。而至于对齐填充是因为HotSpot要求对象的起始地址必须是8字节的整数倍,由此产生了字节对齐问题。

17.对于如何访问对象的实现方案分为两种,一种是使用句柄访问,好处是引用中存储的是稳定的句柄地址,在对象被移动时(在垃圾收集时很普遍)只会改变句柄中的实例数据指针,而引用本身的值不用修改。另一种是直接指针访问,这种方式引用存储的就是对象的地址,好处就是速度更快,因为节省了一次指针定位的时间开销。HotSpot使用直接指针的方式进行对象访问。其实,这两种方案用两张图片就可以很形象的说明:

通过句柄访问对象
通过直接指针访问对象

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值