<
JVM摘抄第三节
>
上一篇

JVM摘抄第四节
下一篇

JVM摘抄第二节

自动内存管理

说道自动内存管理首先说说自动内存管理的弊端吧:
手动内存管理的弊端:容易引起内存泄漏和内存溢出。
同样自动内存管理也有弊端:
自动内存管理的弊端:容易弱化开发人员对问题的敏感和解决问题的能力。(感觉说的挺有道理的)

内存的分配原理

  1. JVM包括三种引用类型,分别是类类型(class type)、数组类型(array type)和接口类型(interface type)。这些引用类型的值分别由类实例、数组实例和实现了某个接口的实现类实例负责创建。
  2. 当语法层面使用new关键字创建一个对象时,JVM会首先检查这个new指令的参数是否能在常量池中定位到一个类的符号引用,然后检查这个符号引用对应的类是否已经成功经历加载、解析和初始化等步骤的操作。当类完成装载步骤之后就已经完全可以确定出创建对象实例所需要的内存空间的大小,接下来JVM会对其进行内存分配。
    Sample Pic

  1. 如果内存空间以规整有序的方式分布,即已用和未用的空间各自一边,彼此之间维系着一个记录下一次分配起始点的标记指针,当为新对象分配内存时,只需要修改指针的偏移量将新对象分配在第一个空闲位置上。这种内存的分配方式叫做指针碰撞。反之,则只能使用空闲列表的方式。(后面会将)
  2. 由于对象实例的创建在JVM中非常频繁,因此在并发情况下从堆区中划分内存空间是非常不安全的。因为堆区和方法区是线程共享的,任何线程都可以访问到这两个区域的共享数据。
  3. TLAB是在Java堆区中的一块线程私有区域,被包含在Eden空间内。
  4. 如果在分配内存之前已经成功完成类加载步骤,JVM会优先选择在TLAB(Thread Local Allocation,本地线程分配内存缓冲区)中为对象实例分配内存空间,我们将这种分配策略称为快速分配策略
  5. 虽然不是所有的对象实例都能在TLAB中成功分配内存,但JVM确实是将TLAB作为内存分配的首选
  6. TLAB空间的内存非常小,缺省状态下仅占整个Eden的1%,当然可以设置它的大小。
  7. 一旦对象在Eden中分配内存失败时,JVM会尝试通过加锁机制确保数据操作的原子性,从而直接在Eden中分配内存。如果Eden中也无法分配内存成功的话,则JVM会执行垃圾回收操作,知道可以在Eden中分配内存为止。(如果是大对象则直接在老年代中分配!)
  8. 当为对象分配好所需的内存空间后,JVM就会初始化对象实例。JVM会首先对分配后的内存空间进行零值初始化,这保证了对象实例字段在java代码中不用赋初始值就可以直接使用,程序能够直接访问到这些字段的数据类型所对应的零值。
  9. 对内存空间进行零值初始化之后,JVM就会初始化头和实例数据(在HotSpot中,内存空间中所存储的对象信息主要包括这两部分),最后将对象引入栈后,再更新PC寄存器中的字节码指令地址,这就完成了一个类的对象实例的创建。

逃逸分析与栈上分配

  1. Java堆区已经不是对象内存分配的唯一选择,如果希望降低垃圾回收的回收频率和提高垃圾回收的效率,那么可以选择堆外存储技术。目前最常见的堆外存储技术就是利用逃逸分析技术分析和筛选出未发生逃逸的对象,然后避开堆区选择直接在栈帧中直接分配内存空间。
  2. 逃逸分析是JVM在执行性能优化之前的一种分析技术,他的目的就是分析出目标的作用域。简单来说,当一个对象被定义在方法体内部后,他的受访权限仅限于方法体内,一旦其引用被外部成员引用,这个对象就因此发生了逃逸。反之,如果定义在方法体内部的对象并没有被任何外部成员所引用时,JVM就会为其在栈帧中分配内存空间。
  3. 由于对象直接在栈上分配内存,栈帧会伴随着方法的创建而创建,伴随方法执行结束而销毁。所以栈上为对象所分配的内存空间会随着栈帧的出帧而释放。
Top
Foot