jvm总结

jvm目录

  • 什么是jvm
  • jvm的组成部分
    • 类加载器
      • 具体过程
      • 双亲委派原则
    • 执行引擎
    • 运行时数据区
      • pc寄存器
      • jvm栈
        • 栈帧![在这里插入图片描述](https://img-blog.csdnimg.cn/30b72acd5c19497db9b80f3377f979a6.png)
      • 本地方法栈
        • oom
        • 组成部分
      • 元空间
      • 运行时常量池
      • 垃圾回收
        • 理论
        • 垃圾回收算法
        • 对象的引用
        • 对象的创建过程

什么是jvm

追本溯源,都知道c和python,c使用编译的方式实现,而python是使用翻译器使用,而java不同,即使用编译又使用翻译器,jvm是满足java跨平台的特性,利用jvm可以将字节码文件(就是编译后的.class)变成计算机可识别的机器码。

jvm的组成部分

jvm包含类加载器,执行引擎,运行时数据区。

类加载器

它是把class文件变成实例的一步,加载过程分为载入验证准备解析初始化。例如实现代码加密来避免核心代码泄漏、解决不同服务依赖同一个包的不同版本所引起的冲突问题以及实现程序热部署来避免调试时频繁重启应用。它遵从双亲委派原则,那什么双亲委派原则了?

具体过程

  • 载入为了后面把class变成class对象它具体做了转入内存。
  • 验证一步为了验证该字节码文件是否符合规范比如关键字限定,是否被初始化,参数和类型,访问修饰符是否正确。
  • 准备就是为了给类变量进行默认初始化(0,null等等)分配内存。
  • 解析为了让常量池中符号引用地址变成实际内存地址。(常量池中符号a 就是符号引用地址,而01112这种就是实际内存地址)。
  • 初始化其实时对类构造器执行,对类变量进行初始化过程。

双亲委派原则

目前类加载器分为:

启动类加载器
BootstrapClassLoader
管理lib

扩展类加载器
ExtClassLoader
lib/ext

应用程序加载器
AppClassLoader
用户类路径

我们也可以自定义类加载器

双亲委派原则,任意一个类加载器都会委托父类去加载,也就所有加载器都优化找顶级父类加载器,好处是避免了重复加载,不同加载器加载内存中出现混乱。

执行引擎

主要为翻译器和jul编译器,jul编译器将字节码流变成本地代码保存效率更高。

运行时数据区

pc寄存器

程序计数器,一块较小的内存空间,是当前线程的所执行的字节码指示器,就是指令地址。不会随程序的运行空间改变,也就不会oom

jvm栈

jvm和寄存器一样是线程私有的,每一个 JVM 线程都有自己的 JVM 栈,这个栈与线程同时创建,它的生命周期与线程相同。

Java 虚拟机栈中每个方法是一个个栈帧,每个栈帧对应一个被调用的方法。当线程执行一个方法时,会创建一个对应的栈帧,并将栈帧压入栈中。当方法执行完毕后,将栈帧从栈中移除。

某些方法中的对象引用没有被返回或者未被外面使用,可以在栈上分配内存。

栈帧jvm总结_第1张图片

本地方法栈

JVM 可能会使用到传统的栈来支持 Native 方法(C语言)的执行,这个栈就是本地方法栈。

在 JVM 中,堆是可供各条线程共享的运行时内存区域,也是供所有类实例和数据对象分配内存的区域。此内存区域的唯⼀⽬的就是存放对象实例, Java ⾥“⼏乎”所有的对象实例都在这⾥分配内存。在jdk1.8后字堆只包含字符串常量池。

oom

当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生该错误。
假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发该错误。和本机的物理内存无关,和我们配置的虚拟机内存大小有关!

组成部分

大部分我们创建的对象,都属于生命周期比较短的,所以会存放在新生代。新生代又细分 Eden 空间、From Survivor 空间、To Survivor 空间,我们创建的对象优先在 Eden 分配。

当eden满了,发生minor gc ,清空未引用对象,存活对象进入from
survivor ,当后面在eden满了,再次minor gc ,eden和from survior中的幸存对象进入to survior,之后就是from 和to空间循环,这也解释为什么使用两个幸存空间,如果只有一个空间,会有空间碎片。

当年龄到达16岁时候,触发幸存者进入老年代,有标准参数-XX:MaxTenuringThreshold默认值是15可以设置。

大对象直接进入老年代,参数值是XX:PretenureSizeThreshold 来设置这些大对象的阈值。

动态年龄判断,除了年龄达到 MaxTenuringThreshold 的值,还有另外一个方式进入老年代,那就是动态年龄判断:在 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。

新生代用复制

当年老年代空间不够用的时候,虚拟机会使用“标记—清除”或者“标记—整理”算法清理出连续的内存空间,分配对象使用。

Full GC: 收集整个堆,包括 新生代,老年代,永久代(在 JDK 1.8及以后,永久代被移除,换为metaspace 元空间)等所有部分的模式

Minor GC 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果大于,则发起 Minor GC。
如果小于,则看 HandlePromotionFailure 有没有设置,如果没有设置,就发起 full gc。
如果设置了 HandlePromotionFailure,则看老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果小于,就发起 full gc。
如果大于,发起 Minor GC。Minor GC 后,看 Survivor 空间是否足够存放存活对象,如果不够,就放入老年代,如果够放,就直接存放 Survivor 空间。如果老年代都不够放存活对象,担保失败(Handle Promotion Failure),发起 full gc。
jvm总结_第2张图片

元空间

在 JVM 中,被加载类型的信息都保存在方法区中。包括类型信息(Type Information)和方法列表(Method Tables)。方法区是所有线程共享的,所以访问方法区信息的方法必须是线程安全的。

运行时常量池

运行时常量池是每一个类或接口的常量池在运行时的表现形式,它包括了编译器可知的数值字面量,以及运行期解析后才能获得的方法或字段的引用。简而言之,当一个方法或者变量被引用时,JVM 通过运行时常量区来查找方法或者变量在内存里的实际地址。

垃圾回收

顾名思义就是释放垃圾占用的空间,防止内存泄露。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。

理论

jvm总结_第3张图片

  • 引用计数法:此时两个对象已经不能再被访问,但其互相持有对对方的引用,如果采用引用计数法,则两个对象都无法被回收。
  • 可达性分析:是通过一系列被称为 GC Roots 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径被称为引用链(Reference Chain),如果某个对象到 GC Roots 间没有任何引用链相连,这代表 GC Roots 到该对象不可达, 此时证明此该对象不可能再被使用。

什么是gc root?
jvm总结_第4张图片

垃圾回收算法

jvm总结_第5张图片

  • 标记清除算法:先把内存区域中的这些对象进行标记,哪些属于可回收标记出来,然后把这些垃圾拎出来清理掉。就像上图一样,清理掉的垃圾就变成未使用的内存区域,等待被再次使用。
  • 复制算法:它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
  • 标记整理算法:不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。

移动起来比较麻烦,效率没有复制算法高,适合于老年代,存活对象比较多情况,移动的情况比较少。

把有效对象放在一边,其他一边直接清理,适用于新生代,移动较少对象,存活比较少的情况

因为内存都是连续使用的空间,内存碎片是个问题

对象的引用

强引用:最传统的引用,如 Object obj = new Object() 。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。

软引用:在内存溢出之前,进行垃圾回收,如果还是内存不足的话,就会oom

弱:只要发生垃圾回收,就会被回收

虚:只是垃圾回收发个通知

对象的创建过程

jvm总结_第6张图片

你可能感兴趣的:(jvm,java,算法)