jVM

JVM是可运行Java代码的假想计算机 ,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆 和 一个存储方法域。JVM是运行在操作系统之上的,它与硬件没有直接的交互

JVM内存空间

JVM内存空间包含:方法区、java堆、java栈、本地方法栈。

方法区是各个线程共享的区域,存放类信息、常量、静态变量。

java堆也是线程共享的区域,我们的类的实例就放在这个区域,可以想象你的一个系统会产生很多实例,因此java堆的空间也是最大的。如果java堆空间不足了,程序会抛出OutOfMemoryError异常。

java栈是每个线程私有的区域,它的生命周期与线程相同,一个线程对应一个java栈,每执行一个方法就会往栈中压入一个元素,这个元素叫“栈帧”,而栈帧中包括了方法中的局部变量、用于存放中间状态值的操作栈,这里面有很多细节,我们以后再讲。如果java栈空间不足了,程序会抛出StackOverflowError异常,想一想什么情况下会容易产生这个错误,对,递归,递归如果深度很深,就会执行大量的方法,方法越多java栈的占用空间越大。

本地方法栈角色和java栈类似,只不过它是用来表示执行本地方法的,本地方法栈存放的方法调用本地方法接口,最终调用本地方法库,实现与操作系统、硬件交互的目的。

PC寄存器,说到这里我们的类已经加载了,实例对象、方法、静态变量都去了自己改去的地方,那么问题来了,程序该怎么执行,哪个方法先执行,哪个方法后执行,这些指令执行的顺序就是PC寄存器在管,它的作用就是控制程序指令的执行顺序。

JVM内存可见性

解决问题:线程2通过改变变量值控制线程1的执行,会出现意想不到的问题
解决办法:加入volatile后,保证内存可见性,原理由硬件支持

指令重排序

加入内存屏障,Volatile相当于synchronized的一个弱实现,他实现了synchronized的语义却没有锁机制

GC回收算法

“stop the world” 每次执行GC时,会暂停java程序的执行,所以有时候程序一卡一卡的

  1. 引用计数法(弃用):影响性能,无法解决循环引用的问题
  2. 标记清除:遍历所有节点,标记所有可达对象,未标记的会被清楚;遍历性能低,留下来的内存不连续
  3. 标记压缩:对内存空间进行压缩,节省内存空间,解决了标记清除算法内存不连续的问题。
  4. 复制算法:往往用于内存空间中新生代的垃圾回收,因为新生代中存活对象较少,复制成本较低。

java堆内存结构

java堆内存结构包括:新生代和老年代,其中新生代由一个伊甸区和2个幸存区组成,2个幸存区是大小相同,完全对称的,没有任何差别。我们把它们称为S0区和S1区,也可以称为from区和to区。

  1. 串行收集器:使用单线程进行垃圾回收。对新生代的回收使用复制算法,对老年代使用标记压缩算法
  2. 并行收集器:使用多线程进行垃圾回收。
  3. CMS回收器:Concurrent Mark Sweep,并发标记清除。CMS的优点显而易见,就是减少了应用程序的停顿时间,让回收线程和应用程序线程可以并发执行。但它也不是完美的,从他的运行机制可以看出,因为它不像其他回收器一样集中一段时间对垃圾进行回收,并且在回收时应用程序还是运行,因此它的回收并不彻底。这也导致了CMS回收的频率相较其他回收器要高,频繁的回收将影响应用程序的吞吐量。CMS只适用于老年代
  4. G1回收器:Garbage First