jvm基础知识

先上图:

2、jvm垃圾是怎么回收的?

  •     如何标记
    • 引用计数器    jvm通过对对象的引用和释放来加减对象的引用次数,回收时,引用数为0,则回收。注:无法解决相互引用的问题。
    • 可达性分析    jvm通过GC ROOTS来标记对象引用情况,向下搜索,如果引用链上没有对象,则表示可回收。
    • GC Roots包含:
      1)虚拟机栈(栈帧中的局部变量表)中对象的引用。JVM会通过(GC Roots)局部变量表,使用可达性分析算法扫描对象,进行gc。
      2)方法区中,类静态属性的引用。
      3)方法区中,常量对象的引用。
      4)本地方法栈中,JNI(native方法)对象的引用。
      5)HotSpot中,使用OopMap数据结构存储对象内偏移量对应的数据类型,在JIT编译时,在安全点(safe point)记录栈和寄存器中的引用和对应的位置。
      6)Rset
      7)并发标记中的黑色对象或灰色对象。
    • 强、软、弱、虚引用    Object  o = new Object();这种新建对象时强引用,不会轻易被回收。 
      SoftReference<String> s = new SoftReference<String>(new String("adb"));这种是软引用,gc时,可能会回收。软引用对象和ReferenceQueue queue = new ReferenceQueue();引用队列一起使用,可以计算引用的次数和引用是否被回收,通过queue.poll()来判断引用是否还存在。也可以定时清空被回收的引用。
      WeakReference<String> s1 = new WeakReference<String>(new String("a"));这种是弱引用,gc时,可能会回收。和ReferenceQueue queue = new ReferenceQueue();引用队列一起使用,可以计算引用的次数和引用是否被回收,通过queue.poll()来判断引用是否还存在。也可以定时清空被回收的引用。
      虚引用随时会被回收。
      
  • 垃圾回收算法
    • 标记-复制算法   适用于新生代,不适用与长期存活、大对象的老年代。将空间分成同等大小的两块,通过标记复制到另一空间,然后清除已标记的空间的对象。
      优点:没有空间碎片,稳定、高效;
      缺点:空间利用率低。
    • 标记-清除算法  适用于老年代。通过标记要回收的对象,标记完成后再回收标记的对象。
      优点:空间利用率高、稳定、高效;
      缺点:会产生大量的空间碎片,可能提前触发gc。
    • 标记-整理算法  适用于老年代。把还不能回收的对象标记复制到空间的一侧,然后标记完成后,以标记的空间的一边为界,清空另一边。
      优点:空间利用率高,空间碎片少。
  • HotSpot算法实现
    • 枚举根节点   
      • oopMap数组来标记对象引用位置
      • 在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来。
      • 在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。
      • 在OopMap的协助下,HotSpot可以快速且准确地完成GC Roots枚举。
    • 安全点(safepoint)
      • HotSpot没有为每条指令都生成oopMap,在枚举根节点的基础上,在特定的位置记录这些信息,这些位置称为安全点。可避免过多的枚举根节点占用过多的空间。
      • 安全节点的位置
        • 方法的末尾
        • 循环的末尾
        • 调用方法的call之后
        • 抛出异常的时候
      • 中断类型   
        • 主动式中断   所有的线程都在执行,当线程执行到安全点时再进行中断。
        • 抢先式中断   先将所有的线程中断,如果是在非安全点上的线程,则让它执行到安全点后再中断。
      • 需注意事项,JIT优化可能导致大循环块造成的停顿时间较长。所有线程到达安全点之后,开始GC。
    • 安全区域(region)
      • Sleep状态或者Blocked状态的线程放在安全区域里

    注:了解安全点的位置,是为了更好的编写代码,比如不能把所有的业务都放在同一个方法里,循环列表要注意大循环对象的循环时间,捕获异常并抛出异常等等。

  • 垃圾收集器
    • Serial收集器
      • 串行的新生代收集器
      • 标记-复制算法
      • 稳定、高效
      • 停顿时间较长
    • Serial Old
      • 串行的老年代收集器
      • 标记-整理算法
      • 稳定高效
      • 停顿时间较长
    • ParNew
      • 并行的新生代收集器
      • 标记-复制算法
      • 大致与Serial收集器一致,只加了并行
    • Parallel Scavenge
      • 并行的新生代收集器
      • 标记-复制算法
      • 更注重吞吐量,可控的吞吐量
    • Parallel Old
      • 并行的老年代收集器
      • 标记-整理算法
    • CMS
      • 6个阶段,初始标记阶段,并发标记阶段,并发预清理阶段(可中断的预清理,中间的小阶段执行young gc,可设置清理时间),重新标记阶段阶段,并发清理阶段,并发重置阶段
      • 适用B/S和客户端的C/S的程序
      • 两次stw,初始标记和重新标记时,重新标记stw的时间比较长主要发生在young gc上;当老年代达到一定比例时,开始回收;当垃圾收集一定次数后开始整理空间碎片。
      •  -XX:+CMSParallelInitialMarkEnabled 线程数设置(一般与cpu核数一致)
      • -XX:+UseCMSCompactAtFullCollection  -XX:+CMSFullGCsBeforeCompaction=0  几次gc后进行一次空间碎片整理。优化空间碎片的整理方案
      • 标记-清除算法,会产生大量空间碎片。
      • -XX:CMSInitiatingOccupancyFactory    参数指定CMS垃圾回收器在老年代达到92%的时候开始工作
      • -XX:+CMSScavengeBeforeRemark  重新标记前,做一次young gc
      • -XX:+CMSMaxAbortablePrecleanTime  执行可中断预清理的时间超过了这个值
    • G1GC
      • 5个阶段,初始标记阶段(initial mark,stw),根区域扫描阶段(root region scan),并发标记阶段(Concurrent Marking),再标记阶段(remark,stw),清除垃圾阶段(Clean up,stw)
      • 两次停顿,初始标记和再标记时进行stop the world
      • Humongous区-存放短期的大对象数据,当对象超过区域的50%时,算是大对象,大对象直接放在H区。如果对象超过一个区域,则需要连续的H区来存放对象,没有足够的空间则进行GC;可避免老年代gc的性能影响。
      • 通过不断的测试优化,减少full gc的次数
      • 设置每个区域的内存大小,最多2048块
      • -XX:MaxGCPauseMillis=200  可设置最大停顿时间,更关注吞吐量和停顿时间。
      • 当老年代占用空间超过整堆比IHOP阈值-XX:InitiatingHeapOccupancyPercent(默认45%)时,G1就会启动一次混合垃圾收集周期。“-XX:G1MixedGCCountTarget”参数,就是在一次混合回收的过程中,最后一个阶段执行几次混合回收,默认值是8次:意味着最后一个阶段,先停止系统运行,混合回收一些Region,再恢复系统运行,接着再次禁止系统运行,混合回收一些Region,反复8次;“-XX:G1HeapWastePercent”,默认值是5%:在混合回收的时候,对Region回收都是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会 立即停止混合回收,意味着本次混合回收就结束;
      • “-XX:G1MixedGCLiveThresholdPercent”,默认值是85%,意思就是确定要回收的Region的时候,必须是存活对象低于85%的Region才可以进行回收

      • G1 Mixed GC  不仅会回收新生代的数据,也会回收部分老年代的对象。

3、类加载机制

  • 类的加载过程   加载》验证》准备》解析》初始化》使用》卸载。
  • 类的加载   最常见的一种情况是将已存在的类的Class文件(也就是字节码文件)从磁盘上面加载到内存里面,将其放在运行时数据区的方法区中,然后在内存中创建一个java.lang.Class对象用来封装类在方法区中的数据结构
  • 类的链接   
    • 验证  确保被加载的class文件的准确性  1、类文件的结构检查,2、语义检查,3、字节码验证,4、二进制兼容性验证。
    • 准备  为类的静态变量(也可以称为类变量)分配内存,并将其初始化为默认值(比如int 的默认值就是0)
    • 解析  将类中的符号引用转换为直接引用
  • 类的初始化   为类的静态变量进行赋值(从代码从上到下执行)
  • 类的使用
    • 主动使用   1、创建类的实例,2、访问某个类或接口的静态变量,或者对该静态变量赋值,3、调用类的静态方法,4、反射(如class.forName())5、初始化一个类的子类,6、Java虚拟机启动时被表明为启动类的类,7、JDK1.7开始提高的动态语言支持。
    • 被动使用   除了以上7种情况,其他使用Java类的方式都被看做是对类的被动使用,都不会导致类的初始化。
  • 类的加载器
    • BootstrapClassLoader   根类加载器,也称为启动类加载器,加载java_home/lib里的基础jar包。
    • ExtClassLoader  拓展类加载器,加载java_home/lib/ext里的jar包。
    • ApplicationClassLoader  系统类加载器,也称为应用类加载器,加载指定目录中的.class文件。
    • 自定义类加载器,java.lang.ClassLoader的子类(所有用户自定义的类加载器都应该继承抽象类ClassLoader类)
    • 双亲委托机制   自定义加载器加载某个类的时候,系统会委托父级类加载器先去加载(自定义类加载器》系统类加载器》拓展类加载器》根类加载器),父类加载器先判断是否加载过该类,加载过则直接引用,没有加载过则继续向上委托;如果根类加载器也判定没有加载过该类,则去加载该类;当加载失败时,才会由自定义类加载器来加载该类。
    • 注:类会在被使用前进行预加载,并且记录发生错误的class文件(LinkageError),如果该错误类一直没有被调用,则不会发生报错。
  • 获取类加载器的几种途径
    • 获得当前类的ClassLoader:  Class<?> clazz1 = Class.forName("java.lang.String");  System.out.println(clazz1.getClassLoader());
    • 获得当前线程上下文的ClassLoader:    Thread.currentThread().getContextClassLoader();
    • 获得系统ClassLoader:  ClassLoader.getSystemClassLoader();
    • 获得调用者的ClassLoader   DriverManager.getCallerLoader();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值