java虚拟机小结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JYL15732624861/article/details/81983041

画图用百度脑图画的,很方便,but没有关联线,添加在备注了,图片上显示不出来,后面文字补充。
这里写图片描述

脑图补充

想到JVM 首先就想到三大块:内存分析、垃圾回收机制、类加载 ,一个个来看。

1.方法区,又叫永久代,存储类信息、编译代码、常量、静态变量,1.8以后取消,取消永久代的原因是什么?

  • 容易出现性能问题,和内存溢出;
  • 类及方法信息比较难确定大小,分配空间小永久代溢出,大了老年代溢出;
  • 给GC带来不便,效率降低

2.方法区和堆是线程共享的,虚拟机栈,本地方法栈和程序计数器是线程独享的。
3.每个区会出现的内存溢出的异常及解决方法:

  • 堆内存溢出 java.lang.OutOfMemoryError: Java heap space
    新产生的对象最初分配在新生代,新生代满后会进行一次Minor GC,如果Minor GC后空间不足会把该对象和新生代满足条件的对象放入老年代,老年代空间不足时会进行Full GC,之后如果空间还不足以存放新对象则抛出OutOfMemoryError异常。常见原因:内存中加载的数据过多如一次从数据库中取出过多数据;集合对对象引用过多且使用完后没有清空;代码中存在死循环或循环产生过多重复对象;堆内存分配不合理;网络连接问题、数据库问题等。
  • 虚拟机栈/本地方法栈溢出 两种情况:1、StackOverflowError当线程请求的栈的深度大于虚拟机所允许的最大深度,则抛出StackOverflowError,栈是当前线程,栈的深度是当前线程中调用的方法,栈帧过多时抛异常解决方法就是适当加大栈的深度,也就是把-Xss的值设置大一些。 2、 java.lang.OutOfMemoryError: unable to create new native thread 与上一种情况相反,这是线程没有地方了,也就是栈没地方放,这时候要适当减小每个栈的深度,也就是把-Xss的值设置小一些。
  • 方法区溢出 java.lang.OutOfMemoryError: PermGen space
  • 本地直接内存溢出 本机直接内存(DirectMemory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但JDK1.4中新增加了NIO,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。,也可能会出现内存溢出的异常。

4.新生代 Eden、survivor from、survivor To 空间大小为什么是8:1:1 是由垃圾回收机制决定的,新生代用复制算法回收,而且存储的对象存活率低,朝生夕死,这样的比例提高效率


1、垃圾回收机制步骤
判断生死-> 选择垃圾收集算法 ->选择时间-> 选择垃圾收集器
2、什么样的节点称为GC Roots
3、什么情况下被回收?为什么?

  • 大多数情况下,新的对象都分配在Eden区,当Eden区没有空间进行分配时,将进行一次Minor GC,清理Eden区中的无用对象。清理后,Eden和From Survivor中的存活对象如果小于To Survivor的可用空间则进入To Survivor,否则直接进入老年代);Eden和From Survivor中还存活且能够进入To Survivor的对象年龄增加1岁(虚拟机为每个对象定义了一个年龄计数器,每执行一次Minor GC年龄加1),当存活对象的年龄到达一定程度(默认15岁)后进入老年代,可以通过-XX:MaxTenuringThreshold来设置年龄的值。
  • 当进行了Minor GC后,Eden还不足以为新对象分配空间(大对象),新对象直接进入老年代。
  • 占To Survivor空间一半以上且年龄相等的对象,大于等于该年龄的对象直接进入老年代,比如Survivor空间是10M,有几个年龄为4的对象占用总空间已经超过5M,则年龄大于等于4的对象都直接进入老年代,不需要等到MaxTenuringThreshold指定的岁数。
  • 在进行Minor GC之前,会判断老年代最大连续可用空间是否大于新生代所有对象总空间,如果大于,说明Minor GC是安全的,否则会判断是否允许担保失败,如果允许,判断老年代最大连续可用空间是否大于历次晋升到老年代的对象的平均大小,如果大于,则执行Minor GC,否则执行Full GC。
  • 永久代(方法区)中用于存放类信息,jdk1.6及之前的版本永久代中还存储常量、静态变量等,当永久代的空间不足时,也会触发Full GC,如果经过Full GC还无法满足永久代存放新数据的需求,就会抛出永久代的内存溢出异常。

4、CMS原理


1、类的生命周期
加载-> 链接(验证、准备、解析)-> 初始化->使用 –>卸载
2、对象的创建过程

  • 虚拟机遇到一条new指令,首先去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化。如果没有,那么必须先执行类的初始化过程。
  • 类加载检查通过后,虚拟机为新生对象分配内存。如果垃圾收集器选择的是Serial、ParNew这种基于压缩算法的,那么内存规整采用指针碰撞法分配内存;如果垃圾收集器选择的是CMS这种基于标记-清除算法,内存不规整,用空闲列表法分配内存
  • 内存分配结束,虚拟机将分配到的内存空间都初始化为零值(不包括对象头)。这一步保证了对象的实例字段在Java代码中可以不用赋初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值。
  • 对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息,这些信息存放在对象的对象头中。
  • 执行方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来

3、java类加载器

  • 启动类加载器
  • 扩展类加载器
  • 应用程序类加载器
  • 自定义类加载器

  • 双亲委派模式

    • 双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会加载类,而是把这个请求委派给它上一层的父加载器,最终请求会传到启动类加载器,然后从启动类加载器开始尝试加载类,如果加载不到(要加载的类不在当前类加载器的加载范围),就让它的子类尝试加载,每层都是如此。

    • 那么双亲委派模型有什么好处呢?最大的好处就是它让Java中的类跟类加载器一样有了“优先级”。前面说到了对于每一个类,都需要由加载它的加载器和这个类本身共同确立这个类在Java虚拟机中的唯一性,比如java.lang.Object类(存放在JAVA_HOMElib t.jar中),如果用户自己写了一个java.lang.Object类并且由自定义类加载器加载,那么在程序中是不是就是两个类?所以双亲委派模型对保证Java稳定运行至关重要。

猜你喜欢

转载自blog.csdn.net/JYL15732624861/article/details/81983041