首页 > 其他分享 >jvm相关知识

jvm相关知识

时间:2023-03-20 14:25:05浏览次数:29  
标签:finalize 知识 回收 线程 内存 jvm 相关 加载 引用

JVM相关知识

  • 内存结构
  •         第一部分:
    • 编译器(将.java源文件编译为.class文件)
    • 类加载器
      • 类加载实际上包括三部分:
        • 懒加载
        • 链接
        • 懒初始化
    • 第二部分:
      • 方法区(线程公有):方法区实际上是JVM的一种规范,它在HotSpot虚拟机(HSDB,Hotspot Debugger调试工具)中,在1.7之间存在于永久代中,在1.8中存在于元空间中,使用本地内存对这些信息进行存储
      • 堆内存(线程公有):jvm调优的重点区域,jvm参数  -Xmx -Xms   -Xmn .主要分为伊甸园、幸存区(from、to)、老年代.   Minor GC、Mixed GC、Full GC
      • 程序计数器(线程私有):程序计数器是唯一一个不存在内存溢出或内存泄漏的区域
      • 虚拟机栈(线程私有):虚拟机栈采用了栈的思想,分为多个栈帧,每一个栈帧对应一个方法。
        • 虚拟机栈中存在的内存泄漏有循环终止条件没有设计好,或者单个栈帧运行所需的空间足够大
        • 本地方法栈(线程私有):本地方法栈对应本地方法接口,调用本地方法中的参数。
    • 第三部分:执行引擎
      • 解释器:将.class字节码文件解释为计算机运行的机器码
      • 即时编译器(JIT):即时编译器会对热点代码的执行进行优化,并将优化后的存放在缓存中
      • GC垃圾回收器:JVM调优的根本,Gc大大地解放了程序员自身对于内存的管理,但仍存在一些问题。
    • 第四部分: 本地方法接口(操作系统、C++实现)
  • 内存模型
    • 内存模型实际上是jvm在多线程下和cpu之间交互的一种设计思想,主要体现为操作系统中的主内存和每个线程之间的高速缓冲区.第一次会从操作系统中读到高速缓存区中,下一次则直接从高速缓冲区中读取,但是进行写操作需要更新主内存后即时地更新(或删除)高速缓冲区的数据
  • 类加载器、双亲委派机制
    • 类加载器包括引导类加载器(根类加载器  / jre/lib包下)、扩展类加载器(/jre/lib/ext)、应用程序类加载器(/classpath)、自定义类加载器。
    • 双亲委派模型指的是加载类时,向上抛出,逐个加载直到能够加载则停止
  • 垃圾回收GC
    • 3种垃圾回收算法
      • 标记清除(速度快、会有内存碎片产生、现在的垃圾回收器已经弃用)
      • 标记整理(速度慢、没有内存碎片产生)
      • 标记复制(没有内存碎片产生、但是内存占用较大,且其中一份内存始终不存储数据)
    • 3种垃圾回收器
      • Parallel并行,注重吞吐量
      • CMS(Concurrent Modify Sweep)垃圾回收(使用标记清除),该回收器已经被弃用,注重响应时间。
      • G1垃圾回收器,同时注重了吞吐量和响应时间
        • 把堆内存分为很多大小相等的内存空间(64份???),每一份都能作为伊甸园、幸存区(合并了from to)、老年代
  • 四种引用
    • GC root、可达性分析、三色标记算法(标记过的、没有被标记的,正在进行分析标记的)
      • 强引用:被强引用,即普通的new一个对象的引用在垃圾回收时,是不会被回收的。(包括直接饮用的其内部的成员变量)
      • 软引用:  在第一次垃圾回收时,不会被回收,当再次垃圾回收的时候,就会被回收
      • 弱引用: 在第一次进行垃圾回收的时候,就会被回收。
      • 虚引用:在第一次进行垃圾回收的时候,就会被回收。
      • 引用队列:对于软引用、弱引用和虚引用来说,其创建的中间引用对象都可以加入到引用队列中,然后开启一个单独的线程处理该引用队列。其中虚引用是必须强制使用引用队列的。
      • ThreadLocal的内存泄漏和弱引用:
        • ThreadLocal中的key被设计为弱引用,因此其key可以被进行垃圾回收,但是回收之后,我们就存在无法根据key去手动remove其对应的value,而value却是强引用,此时就存在内存泄漏问题。
        • 其解决思路是:
        • 1.在使用完毕后,及时的手动移除value
        • 2.将弱引用加入到引用队列中,其中存储的是ThreadLocalMap中的Entry对象,通过将Entry对象置为null,实现key和value都被回收。
        • 3.Jdk9中对于2的实现原理进行了优化和封装,使用Cleaner对象(运行时是一个守护线程)
  • finalize方法剖析
      • finalize
        • 它是 Object 中的一个方法,如果子类重写它,垃圾回收时此方法会被调用,可以在其中进行资源释放和清理工作.
        • 但是将资源释放和清理放在 finalize 方法中非常不好,非常影响性能,严重时甚至会引起 OOM,从 Java9 开始就被标注为 @Deprecated,不建议被使用了
        • finalize 原理
          • 对 finalize 方法进行处理的核心逻辑位于 java.lang.ref.Finalizer 类中,它包含了名为 unfinalized 的静态变量(双向链表结构),Finalizer 也可被视为另一种引用对象(地位与软、弱、虚相当,只是不对外,无法直接使用)
          • 当重写了 finalize 方法的对象,在构造方法调用之时,JVM 都会将其包装成一个 Finalizer 对象,并加入 unfinalized 链表中
            • Finalizer 类中还有另一个重要的静态变量,即 ReferenceQueue 引用队列,刚开始它是空的。当狗对象可以被当作垃圾回收时,就会把这些狗对象对应的 Finalizer 对象加入此引用队列
            • 但此时 Dog 对象还没法被立刻回收,因为 unfinalized -> Finalizer 这一引用链还在引用它嘛,为的是【先别着急回收啊,等我调完 finalize 方法,再回收】
            • FinalizerThread 线程会从 ReferenceQueue 中逐一取出每个 Finalizer 对象,把它们从链表断开并真正调用 finallize 方法
            • 由于整个 Finalizer 对象已经从 unfinalized 链表中断开,这样就会在下次 gc 时就被回收了
        • finalize 缺点
          • 无法保证资源释放:FinalizerThread 是守护线程,代码很有可能没来得及执行完,线程就结束了
          • 无法判断是否发生错误:执行 finalize 方法时,会吞掉任意异常(Throwable)
          • 内存释放不及时:重写了 finalize 方法的对象在第一次被 gc 时,并不能及时释放它占用的内存,因为要等着 FinalizerThread 调用完 finalize,把它从 unfinalized 队列移除后,第二次 gc 时才能真正释放内存
          • 有的文章提到【Finalizer 线程会和我们的主线程进行竞争,不过由于它的优先级较低,获取到的CPU时间较少,因此它永远也赶不上主线程的步伐】这个显然是错误的,FinalizerThread 的优先级较普通线程更高,原因应该是 finalize 串行执行慢等原因综合导致
  • 内存溢出的几种情况
    • 典型情况
      • 误用线程池导致的内存溢出
        • 使用了不推荐的线程池创建
          • 固定线程数的线程池(Executors.newFixedThreadPool,其中的工作队列是近似没有上限的(Integer的最大值))
          • 带有缓存的线程池(Executors.newCachedThreadPool,其中的核心线程数为0,全部都是救济线程(Integer的最大值)
        • 单次查询数据量太大(禁止调用查询全表的数据接口)导致的内存溢出,进行分页查询(limit)
        • 动态生成类导致的内存溢出(动态生成的类使用了自定义的类加载器,其中的静态成员变量是强引用,不能被垃圾回收,应该将静态变量修改为成员变量)

标签:finalize,知识,回收,线程,内存,jvm,相关,加载,引用
From: https://www.cnblogs.com/zmmijava/p/17236107.html

相关文章

  • tfrecord的相关操作
    读取tfreocrd的内容和键值。在tf2里没有找到合适的原生函数,还是调用的v1的包forexampleintf.compat.v1.python_io.tf_record_iterator("data/foobar.tfrecord"):p......
  • Qt 时间戳和时间相关的转换操作
    chatgpt1.获取时间戳#include<QDateTime>#include<QDebug>qint64timestamp=QDateTime::currentDateTime().toSecsSinceEpoch();qDebug()<<"当前时间戳:"<<ti......
  • Java线程知识点总结
    文章目录​​Java线程基础​​​​线程简介​​​​什么是进程​​​​什么是线程​​​​进程和线程的区别​​​​创建线程​​​​Thread​​​​Runnable​​​​Calla......
  • 移动加权商品,成本计算相关(6.5之前版本适用)
    6.5之前版本适用移动加权商品,如果发现成本不对,请先进行成本计算。成本计算的注意事项:1、成本计算的过程中不要开单2、服务器内存使用情况不要超过90%,因为计算的过程中......
  • 关于存储的一些知识
    1一些概念 SATA,IDE,SSD,HDD, AHCI,IntelRST,IntelVMD,Raid,PCleNVmeSSD,从接口类型分硬盘,SATA或者IDE首先说SATA(SerialadvancedtTechnologyAttachment......
  • pnpm安装相关内容
    npm全局安装:npminstallpnpm-g 设置源//查看源pnpmconfiggetregistry//切换淘宝源pnpmconfigsetregistryhttps://registry.npmmirror.com/安装如......
  • Nginx服务配置及相关模块
    一、Nginx配置文件1、主配置文件解析①yum安装主配置文件位置:/etc/nginx/nginx.conf②编译安装主配置文件位置:/编译安装路径/conf/nginx.confnginx主配置文件:nginx.co......
  • 自己动手从零写桌面操作系统GrapeOS系列教程——19.硬盘读写理论知识
    学习操作系统原理最好的方法是自己写一个简单的操作系统。一、硬盘控制器我们前面已经讲过硬盘控制器是一种I/O接口,CPU通过它就能间接的读写硬盘。硬盘控制器主要有ID......
  • JVM 是如何处理异常的?
    在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给JVM的过程称为抛......
  • biopython Sequence相关
    参考:http://biopython.org/DIST/docs/tutorial/Tutorial.html1.构建Seq()对象fromBio.SeqimportSeqmyseq=Seq("AGTACACTCA")print(myseq)#AGTACACTCAprint(typ......