JVM内存区域

运行时数据区域

JVM运行内存

线程私有的:

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈

线程共享的:

  • 方法区
  • 直接内存 (非运行时数据区的一部分)

程序计数器

线程私有的,每一个线程都有一个自己的计数器,可以看作是当前线程所执行的字节码的行号指示器。

记录命令执行到第几行,记录代码执行位置。

通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

Java虚拟机栈

线程私有的,每一个线程都有一个自己的虚拟机栈,每一次方法调用,就会创建一个对应的栈帧,栈帧入栈执行代码,执行完毕出栈。

生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡。

本地方法栈

线程私有的,每一个线程都有一个自己的本地方法栈,与Java虚拟机栈类似。

Java虚拟机栈加载的是普通方法,本地方法加载的是native修饰的方法

native:在java中用native修饰的,表示这个方法不是java原生的

线程共享,存放new的对象实例,几乎所有的对象实例及数组在这分配内存;字符串常量

JDK1.8后堆内存:

  1. 新生代:新创建的对象
  2. 老年代:经过新生代多次垃圾回收存活下来的对象存在老年代中
  3. 元空间:1.8后取代原来的永久代,即保存原来永久代的对象

元空间

存储.class信息,类的信息,方法的定义,静态变量等

GC垃圾回收

JVM垃圾回收分为两步,如何发现垃圾,如何回收垃圾。

线程私有不存在垃圾回收,只有线程共享的才会存在垃圾回收。

如何发现垃圾

引用计数法

给对象添加一个引用计数器

  • 每被引用一次,计数器加1
  • 引用失效,计数器减1
  • 为0时当作垃圾回收

优点:简单,效率高,快

缺点:无法解决对象相互引用的问题

可达性分析算法(根搜索)

以一个节点GC Roots为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的话,则证明此对象是不可用的,需要被回收

可作为GC Roots的有:

  • 虚拟机栈中引用的对象
  • 本地方法栈中引用的对象
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象

如何回收垃圾

标记-清除算法

分为“标记”和“清除”两个阶段,首先标记访问过的对象,标记完成后统一回收所有没有标记的对象

缺点: 效率不高,标记清除后产生大量不连续的内存碎片

复制算法

为了解决效率问题,把内存划两等份,每次用其中一块,满了后把存活的对象copy到另一边去

缺点: 内存只有原来一半

标记-整理算法

让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存,不会产生大量不连续碎片

分代收集算法(主流)

根据对象存活周期的不同采用不同算法

新生代:回收频率高,只有少量对象存活,采用标记-复制

老年代:对象存活率高,采用标记-整理