从0开始回顾JVM---系列二

简介: 11、什么情况下会发生内存泄露?1. 静态集合类引起内存泄漏● 静态集合的生命周期和 JVM 一致,所以静态集合引用的对象不能被释放。2. 单例模式● 单例对象在初始化后会以静态变量的方式在 JVM 的整个生命周期中存在。如果单例对象持有外部的引用,那么这个外部对象将不能被 GC 回收,导致内存泄漏。3. 数据连接、IO、Socket等连接● 创建的连接不再使用时,需要调用 close 方法关闭连接,只有连接被关闭后,GC 才会回收对应的对象(Connection,Statement,ResultSet,Session)。忘记关闭这些资源会导致持续占有内存,无法被 GC 回收。4

11、什么情况下会发生内存泄露?

  1. 静态集合类引起内存泄漏
  • 静态集合的生命周期和 JVM 一致,所以静态集合引用的对象不能被释放。
  1. 单例模式
  • 单例对象在初始化后会以静态变量的方式在 JVM 的整个生命周期中存在。如果单例对象持有外部的引用,那么这个外部对象将不能被 GC 回收,导致内存泄漏。
  1. 数据连接、IO、Socket等连接
  • 创建的连接不再使用时,需要调用 close 方法关闭连接,只有连接被关闭后,GC 才会回收对应的对象(Connection,Statement,ResultSet,Session)。忘记关闭这些资源会导致持续占有内存,无法被 GC 回收。
  1. 变量不合理的作用域
  • 一个变量的定义作用域大于其使用范围,很可能存在内存泄漏;或不再使用对象没有及时将对象设置为 null,很可能导致内存泄漏的发生。
  1. hash值发生变化
  • 对象Hash值改变,使用HashMap、HashSet等容器的时候,由于对象修改之后的Hash值和存储进容器时的Hash值不同,所以无法找到存入的对象,自然也无法单独删除了,这也会造成内存泄漏。说句题外话,这也是为什么String类型被设置成了不可变类型。
  1. ThreadLocal使用不当
  • ThreadLocal的弱引用导致内存泄漏也是个老生常谈的话题了,使用完ThreadLocal一定要记得使用remove方法来进行清除。

12、谈谈OOM的认识?如何排查 OOM 的问题?

除了程序计数器,其他内存区域都有 OOM 的风险,发生的地方如下:

  • Java虚拟机栈+本地方法栈,扩展时无法申请到足够内存,OOM((线程请求深度超过JVM允许栈深度,StackOverflowException) 。
  • 栈帧过多导致栈内存溢出,方法递归调用时,没有设置正确的递归结束条件,导致无限递归。
  • 栈帧过大导致栈内存溢出
  • 无限递归,第三方代码中出现两个类中循环引用的问题,导致无限递归。
  • 堆内存溢出不断产生新的对象,且对象都被引用了,无法触发垃圾回收机制,长时间就会导致堆内存溢出。
  • 方法区 方法区如果申请内存时发现内存不足,方法区也会出现内存溢出的问题,经常会遇到的是动态生成大量的类,无法满足内存分配需求时。
  • 1.8以前会导致永久内存溢出: java.lang.OutOfMemoryError:PermGen space。
  • 1.8之后会导致元空间内存溢出:元空间默认使用系统内存,且不会设置内存的上限,所以我们要手动设置。
  • 直接内存 OOM直接内存的回收和释放是通过unsafe 对象实现的,不是java 的垃圾回收机制。


排查 OOM 的方法:

  • 增加两个参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof,当 OOM 发生时自动 dump 堆内存信息到指定目录;
  • 同时 jstat 查看监控 JVM 的内存和 GC 情况,先观察问题大概出在什么区域;
  • 使用 MAT 工具载入到 dump 文件,分析大对象的占用情况,比如 HashMap 做缓存未清理,时间长了就会内存溢出,可以把它改为弱引用 。

13、如何判断一个对象是否存活或者是垃圾?


判断一个对象是否存活,分为两种算法,引用计数法和可达性分析算法

  1. 引用计数法: 给每一个对象设置一个引用计数器,当有一个地方引用该对象的时候,引用计数器就+1,引用失效时,引用计数器就-1;当引用计数器为0的时候,就说明这个对象没有被引用,也就是垃圾对象,等待回收;
  • 缺点:无法解决循环引用的问题,当A引用B,B也引用A的时候,此时AB对象的引用都不为0,此时也就无法垃圾回收,所以一般主流虚拟机都不采用这个方法;
  1. 可达性分析法:从一个被称为GC Roots的对象向下搜索,如果一个对象到GC Roots没有任何引用链相连接时,说明此对象不可用,在java中可以作为GC Roots的对象有以下几种
  • 虚拟机栈中引用的对象;
  • 方法区类静态属性引用的变量;
  • 方法区常量池引用的对象;
  • 本地方法栈JNI引用的对象。


14、强引用、软引用、弱引用、虚引用是什么,有什么区别?


强引用:

  • 概念强引用,就是普通的对象引用关系,如 String s = new String("ConstXiong")
  • 回收:只有所有GC Root对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

软引用

  • 概念软引用,用于维护一些可有可无的对象。只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。
  • 回收仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收,回收软引用对象,可以配合引用队列来释放软引用自身。

弱引用

  • 概念弱引用,相比软引用来说,要更加无用一些,它拥有更短的生命周期。
  • 回收仅有弱引用引用该对象,在垃圾回收时,无论内存是否充足,都会回收弱引用对象,可以配合引用队列来释放弱引用自身。

虚引用

  • 概念:虚引用是一种形同虚设的引用,在现实场景中用的不是很多,它主要用来跟踪对象被垃圾回收的活动。
  • 回收必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存。

15、被引用的对象就一定能存活吗?


不一定,需要看引用类型

  1. 仅有弱引用引用该对象,在垃圾回收时,无论内存是否充足,都会回收弱引用对象。
  2. 但是仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收,回收软引用对象。

16、finalize()方法了解吗?有什么作用?


  1. 如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。
  2. 如果对象在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己 (this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它就"逃过一劫";但是如果没有抓住这个机会,那么对象就真的要被回收了。

17、方法区的垃圾回收?


方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型

废弃的常量:

  • 只要常量池中的常量没有被任何地方引用,就可以被回收。
  • 例如:一个字符串"abc”放入常量池,现在没有一个值为"abc"的字符串对象,也就是没有任何字符串对象引用常量池中“abc”的常量,且虚拟机其他地方没有引用这个字面量,如果发生垃圾回收且有必要时,常量会被系统清理出常量池。

不再使用的类型需要同时满足下面3个条件才可以被允许回收

  • 该类的所有实例被回收,堆中不存在该类及其派生子类的实例。
  • 加载该类的ClassLoader被回收
  • 该类对应的java.lang.Class对象没有在任何地方被引用

满足了上面的3个条件,只是被允许回收,不是一定会被回收,还需要设置一些参数进行控制。 虚拟机提供了-Xnoclassgc参数进行控制。

18、Java堆的内存分区了解吗?


  1. 在 Java 中,堆被划分成两个不同的区域新生代 ( Young )、老年代 ( Old ),新生代默认占总空间的 1/3,老年代默认占 2/3。
  2. 新生代有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8 : 1 : 1。
  • 新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。
  • 老年代的垃圾回收(又称Major GC)通常使用“标记-清理”或“标记-整理”算法。

19、 垃圾回收算法有哪些?  


Java中有四种垃圾回收算法,分别是标记清除法、标记整理法、复制算法、分代收集算法

  1. 标记清除:
  • 步骤:第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记; 第二步:再遍历一遍,将所有标记的对象回收掉。
  • 优点垃圾回收速度快。
  • 缺点空间不连续,容易产生内存碎片。
  1. 标记整理
  • 步骤:第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记;第二步:在清理垃圾的过程中,会将可用的对象向前移动,保证内存的紧凑,连续的内存增多了。
  • 优点:没有内存碎片。。
  • 缺点:移动内存,改变地址,速度较慢 。
  1. 复制算法
  • 将内存划分为大小相等的两块区域:From和To,To区域空闲
  • 步骤
  1. 标记;
  2. 把From上存活的对象复制到To区域中,并在复制过程中进行整理;
  3. 此时From上剩余的都是垃圾了,直接清空From;
  4. 交换From和To。
  • 优点: 不会产生内存碎片。
  • 缺点: 占用双倍的内存空间。
  1. 分代收集算法
  • 根据内存对象的存活周期不同,将内存划分成几块,java虚拟机一般将内存分成新生代和老生代,在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;老年代中因为对象的存活率极高,没有额外的空间对他进行分配担保,所以采用标记清理或者标记整理算法进行回收。
相关文章
|
8月前
|
存储 算法 安全
从0开始回顾JVM---系列三
20、有哪几种垃圾回收器,各自的优缺点是什么? ⚡ 垃圾回收器主要分为以下几种:Serial,Parallel,CMS,G1。 1. Serial ● 单线程垃圾回收器,该线程运行时,其他线程都暂停,使用复制算法; ● 使用场景: 堆内存较小,适合个人电脑(CPU核数较小); a. 工作在老年区,回收算法:标记整理;工作在新生代,回收算法:复制; b. 单线程的垃圾回收器,只有一个垃圾回收线程在运行; c. 垃圾回收线程结束后,其他线程恢复运行; d. 触发垃圾回收时让所有线程在安全点停下【在垃圾回收过程中,对象的地址可能发生改变,为了保证安全使用对象地址,要
|
8月前
|
存储 安全 Java
从0开始回顾JVM---系列四
虚拟机执行 1、类的生命周期吗? 一个类从被加载到虚拟机内存中开始,到从内存中卸载,整个生命周期需要经过七个阶段:加载 、验证、准备、解析、初始化 、使用 和 卸载,其中验证、准备、解析三个部分统称为连接。 2、什么是类加载?类加载的过程? 类加载: 虚拟机把描述类的数据加载到内存里面,并对数据进行校验、解析和初始化,最终变成可以被虚拟机直接使用的class对象。 类加载过程如下: ● 加载:1、根据类的全限定类名获取二进制字节流;2、将字节流代表的静态存储结构转为方法区运行时存储数据结构;3、在堆中生成Class对象,作为方法区这个类数据访问入口; ● 验证: 检验加载的class文
|
8月前
|
运维 监控 数据可视化
从0开始回顾JVM---系列五
JVM调优 1、有哪些常用的命令行性能监控和故障处理工具? 操作系统工具 ● top:显示系统整体资源使用情况 ● vmstat:监控内存和CPU ● iostat:监控IO使用 ● netstat:监控网络使用 JDK性能监控工具 ● jps:虚拟机进程查看 ● jstat:虚拟机运行时信息查看 ● jinfo:虚拟机配置查看 ● jmap:内存映像(导出) ● jhat:堆转储快照分析 ● jstack:Java堆栈跟踪 ● jcmd:实现上面除了jstat外所有命令的功能 2、了解哪些可视化的性能监控和故障处理工具? 以下是一些JDK自带的可视化性能监控和故障处理工具: ● JCons
|
8月前
|
Java
jvm---类加载器(1)
jvm---类加载器(1)
|
8月前
|
存储 Java 编译器
从0开始回顾JVM---系列一
引言 1、什么是JVM? JVM——Java虚拟机,它是Java实现平台无关性的基石。 Java程序运行的时候,编译器将Java文件编译成平台无关的Java字节码文件(.class),接下来对应平台JVM对字节码文件进行解释,翻译成对应平台匹配的机器指令并运行。 同时JVM也是一个跨语言的平台,和语言无关,只和class的文件格式关联,任何语言,只要能翻译成符合规范的字节码文件,都能被JVM运行。 内存管理 1、什么是JVM内存结构? Java1.8 之后的内存结果图: JVM将虚拟机分为5大区域,程序计数器、虚拟机栈、本地方法栈、java堆、方法区,其中方法区和堆是线程共享区,虚拟
|
8月前
|
存储 算法 Java
JVM(垃圾回收机制 --- GC)
JVM(垃圾回收机制 --- GC)
91 5
|
存储 缓存 监控
JVM详解 --- 垃圾回收机制
JVM详解 --- 垃圾回收机制
JVM详解 --- 垃圾回收机制
|
算法 Java 数据库
Java基础学习---3、堆、GC(一)
Java基础学习---3、堆、GC(一)
|
算法 Java
Java基础学习---3、堆、GC(二)
Java基础学习---3、堆、GC(二)
|
算法 Java API
Java虚拟机System.gc()解析
对于Java语言来说是不用刻意手动去释放内存,同时,也尽可能不需要手动去干预Java虚拟机的GC行为。在本篇文章中,我们试图从多个方面去解析有关System.gc()API调用的最常见问题。希望对需要了解这块技术的朋友有所帮助。
214 0