浅堆、深堆、内存泄露

浅堆

1、Shallow Heap

2、指一个对象所消耗的内存

(1)在 32 位系统中,一个对象引用会占据 4 个字节,一个 int 类型会占据 4 个字节,long 型变量会占据 8 个字节,每个对象头需要占用 8 个字节

(2)根据堆快照格式不同,对象的大小可能会同 8 字节进行对齐

3、JDK 7 中,以 String 为例

int hash32 0
int hash 0
ref value C:\Users\Administrat

(1)2 个 int 值共占 8 字节

(2)对象引用占用 4 字节

(3)对象头 8 字节

(4)合计 20 字节,向 8 字节对齐,共占 24 字节

(5)24 字节为 String 对象的浅堆大小,它与 String 的 value 实际取值无关,无论字符串长度如何,浅堆大小始终是 24 字节

 

保留集

1、Retained Set

2、对象 A 的保留集

(1)指当对象 A 被垃圾回收后,所有可以被释放的对象集合(包括对象 A 本身)

(2)只能通过对象 A 被直接,或间接访问到的所有对象的集合,即仅被对象 A 所持有对象的集合

 

深堆

1、Retained Heap

2、指对象的保留集中,所有对象的浅堆大小之和

(1)浅堆指对象本身占用内存,不包括其内部引用对象的大小

(2)一个对象的深堆:只能通过该对象,所有直接 / 间接访问的对象的浅堆之和,即对象被回收后,可以释放的真实空间

 

对象的实际大小

1、定义:一个对象能触及的所有对象的浅堆大小之和,即通常意义上对象大小

2、实际上该概念和垃圾回收无关

3、对象引用关系(例):对象 A 引用 C、D,对象 B 引用 C、E

(1)对象 A 浅堆大小只是 A 本身,不含 C、D

(2)A 实际大小为 A、C、D 三者之和

(3)A 深堆大小为 A、D 之和

(4)由于对象 C 还可以通过对象 B 访问,因此不在对象 A 的深堆范围内

 

支配树

1、Dominator Tree

2、概念源自图论,MAT 提供支配树(Dominator Tree)的对象图

(1)支配树体现对象实例间的支配关系

(2)支配树基于对象引用图建立

(3)所有指向对象 B 的路径都经过对象 A,则认为对象 A 支配对象 B

(4)如果对象 A 是离对象 B 最近的一个支配对象,则认为对象 A 为对象 B 的直接支配者

3、基本性质

(1)对象 A 的子树(所有被对象 A 支配的对象集合)表示对象 A 的保留集(retained set),即深堆

(2)如果对象 A 支配对象 B,则对象 A 的直接支配者也支配对象 B

(3)支配树的边与对象引用图的边不直接对应

4、例:左图表示对象引用图,右图表示左图所对应的支配树

(1)对象 A、对象 B 由根对象直接支配

(2)因为到对象 C 的路径中,可以经过 A,也可以经过 B,所以对象 C 的直接支配者也是根对象

(3)对象 F、对象 D 相互引用

(4)因为到对象 F 的所有路径必然经过对象 D,所以对象 D 是对象 F 的直接支配者

(5)因为到对象 D 的所有路径中,必然经过对象 C,即使是从对象 F 到对象 D 的引用,从根节点出发,也经过对象 C,所以对象 D 的直接支配者为对象 C

(6)同理,对象 E 支配对象 G

(7)因为到达对象 H 可以通过对象 D,也可以通过对象 E,所以对象 D、对象 E 都不能支配对象 H,而经过对象 C 既可以到达对象 D,也可以到达对象 E,因此对象 C 为对象 H 的直接支配者

 

内存泄漏

1、Memory Leak

(1)严格:只有对象不会再被程序使用,但是 GC 又不能回收的情况

(2)宽泛:对象的生命周期过长,甚至导致 OOM

2、可达性分析算法,判断对象是否不再使用,本质判断一个对象是否还被引用

3、分类

(1)经常发生:发生内存泄露的代码会被多次执行,每次执行,泄露一块内存

(2)偶然发生:在某些特定情况下才会发生

(3)一次性:发生内存泄露的方法只会执行一次

(4)隐式泄漏:一直占着内存不释放,直到执行结束,最终会释放,但执行时间长,可能会导致内存耗尽

 

内存溢出

1、Out Of Memory

2、申请内存时,没有足够的内存可以使用

3、内存泄漏的增多,最终会导致内存溢出

 

Java 中 8 种内存泄露情况

1、静态集合类

(1)如:HashMap、LinkedList 等,如果容器为静态,则其生命周期与 JVM 一致,容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏

(2)长生命周期的对象,持有短生命周期对象的引用,即使短生命周期的对象不再使用,但因为长生命周期对象持有它的引用,而导致不能被回收

2、单例模式

(1)和静态集合导致内存泄露的原因类似

(2)因为单例的静态特性,所以其生命周期和 JVM 的生命周期相同

(3)若单例对象如果持有外部对象的引用,则该外部对象不会被回收,造成内存泄漏

3、内部类持有外部类

(1)一个外部类的实例对象的方法,返回一个内部类的实例对象

(2)该内部类对象被长期引用,即使外部类实例对象不再被使用,但因为内部类持有外部类的实例对象,外部类对象将不会被垃圾回收,造成内存泄漏

4、连接

(1)如:数据库连接、网络连接、I/O 连接等

(2)在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用 close 方法释放与数据库的连接

(3)只有连接被关闭后,垃圾回收器才会回收对应的对象,否则,如果在访问数据库的过程中,对 Connection、Statement、ResultSet 不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏

5、变量不合理的作用域

(1)一般而言,一个变量定义的作用范围大于其使用范围,有可能造成内存泄漏

(2)另一方面,如果没有及时地把对象设置为 null,有可能导致内存泄漏

6、改变哈希值

(1)当一个对象被存储进 HashSet 后,就不能修改该对象中的参与计算哈希值的字段

(2)若对象修改后的哈希值,与最初存储进 HashSet 中的哈希值不同,即使调用 contains 方法,使用该对象的当前引用作为的参数,在 HashSet 中检索对象,将返回找不到对象的结果,也无法从 HashSet 中单独删除当前对象,造成内存泄漏

(3)所以 String 设置为不可变类型,可以把 String 存入 HashSet,或把 String 当做 HashMap 的 key 值

(4)当把自定义的类保存到散列表时,需要保证对象 hashCode 不可变

7、缓存泄露

(1)对象引用放入到缓存中,容易被遗忘

(2)可以使用 WeakHashMap 代表缓存,特点:当除了自身有对 key 的引用外,此 key 没有其他引用,则此 Map 会自动丢弃此值

8、监听器、其他回调

(1)若客户端在实现的 API 中注册回调,却没有显式取消,则会积聚

(2)只保存回调的弱引用,确保回调立即被回收,例如:保存为 WeakHashMap 中的 key

posted @   半条咸鱼  阅读(197)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示