1.年轻代存活的对象太多,老年代了放不下

01.示例代码

public class DemoTest1 {
public static void main(String[] args) {
byte[] array1 = new byte[4 * 1024 * 1024];
array1 = null; byte[] array2 = new byte[2 * 1024 * 1024];
byte[] array3 = new byte[2 * 1024 * 1024];
byte[] array4 = new byte[2 * 1024 * 1024];
byte[] array5 = new byte[128 * 1024]; byte[] array6 = new byte[2 * 1024 * 1024]; }

02.启动JVM参数

-XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

其中,参数-XX:PretenureSizeThreshold,参数要设置大对象阈值为3MB,也就是超过3MB,就直接进入老年代。

大对象大小是3MB。一旦对象大小超过3MB,不会进入新生代,直接进入老年代。

启动命令:

java  -jar -XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8  -XX:MaxTenuringThre
shold=15 -XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log jvm-demo.jar

03.GC日志

启动之后就得到如下GC日志:

Java HotSpot(TM) 64-Bit Server VM (25.151-b12) for windows-amd64 JRE (1.8.0_151-b12), built on Sep  5 2017 19:33:46 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 16703268k(7458748k free), swap 23781156k(9784196k free)
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=3145728 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC
0.174: [GC (Allocation Failure) 0.174: [ParNew (promotion failed): 7457K->8328K(9216K), 0.0046949 secs]
0.179: [CMS: 8194K->6962K(10240K), 0.0033396 secs] 11553K->6962K(19456K), [Metaspace: 2970K->2970K(1056768K)], 0.0089224 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
par new generation total 9216K, used 2130K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 26% used [0x00000000fec00000, 0x00000000fee14930, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
concurrent mark-sweep generation total 10240K, used 6962K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 2976K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 330K, capacity 386K, committed 512K, reserved 1048576K

04.分析GC日志

先看如下代码:

 byte[] array1 = new byte[4 * 1024 * 1024];
array1 = null;

这行代码直接分配了一个4MB的大对象,此时这个对象会直接进入老年代,接着array1不再引用这个对象。

此时内存分配如下:

紧接着就是如下代码

byte[] array2 = new byte[2 * 1024 * 1024];
byte[] array3 = new byte[2 * 1024 * 1024];
byte[] array4 = new byte[2 * 1024 * 1024];
byte[] array5 = new byte[128 * 1024];

连续分配了4个数组,其中3个是2MB的数组,1个是128KB的数组,如下图所示,全部会进入Eden区域中。

接着会执行如下代码:byte[] array6 = new byte[2 * 1024 * 1024];。此时还能放得下2MB的对象吗?

不可能了,因为Eden区已经放不下了。因此此时会直接触发一次Young GC。

我们看下面的GC日志:

0.174: [GC (Allocation Failure) 0.174: [ParNew (promotion failed): 7457K->8328K(9216K), 0.0046949 secs]

这行日志显示了,Eden区原来是有7000多KB的对象,但是回收之后发现一个都回收不掉,因为上述几个数组都被变量引用了。

所以此时,一定会直接把这些对象放入到老年代里去,但是此时老年代里已经有一个4MB的数组了,还能放的下3个2MB的数组和1个128KB的数组吗?

明显是不行的,此时一定会超过老年代的10MB大小。

所以此时看gc日志:

0.179: [CMS: 8194K->6962K(10240K), 0.0033396 secs] 11553K->6962K(19456K), [Metaspace: 2970K->2970K(1056768K)], 0.0089224 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

此时执行了CMS垃圾回收器的Full GC,Full GC其实就是会对老年代进行Old GC,同时一般会跟一次Young GC关联,还会触发一次元数据区(永久代)的GC。

在CMS Full GC之前,就已经触发过Young GC了,此时可以看到此时Young GC就已经有了,接着就是执行针对老年代的Old GC,也就是如下日志:

CMS: 8194K->6962K(10240K), 0.0033396 secs

这里看到老年代从8MB左右的对象占用,变成了6MB左右的对象占用,这是怎么个过程呢?

很简单,一定是在Young GC之后,先把2个2MB的数组放入了老年代,如下图。

此时要继续放1个2MB的数组和1个128KB的数组到老年代,一定会放不下,所以此时就会触发CMS的Full GC。

然后此时就会回收掉其中的一个4MB的数组,因为他已经没人引用了,如下图所示。

所以再看CMS的垃圾回收日志:CMS: 8194K->6962K(10240K), 0.0033396 secs,他是从回收前的8MB变成了6MB,就是上图所示。

最后在CMS Full GC执行完毕之后,其实年轻代的对象都进入了老年代,此时最后一行代码要在年轻代分配2MB的数组就可以成功了,如下图。

05.总结

这是一个触发老年代GC的案例,就是年轻代存活的对象太多放不下老年代了,此时就会触发CMS的Full GC。

2.老年代可用空间小于了历次Young GC后升入老年代的对象的平均大小

01.示例代码

public class DemoTest1 {
public static void main(String[] args) {
byte[] array1 = new byte[1 * 1024 * 1024];
array1 = null;
byte[] array2 = new byte[1 * 1024 * 1024];
array2 = null;
byte[] array3 = new byte[1 * 1024 * 1024];
array3 = null;
byte[] array4 = new byte[1 * 1024 * 1024];//触发YGC 1MB 1 array1 = new byte[1 * 1024 * 1024];
array1 = null;
array2 = new byte[1 * 1024 * 1024];
array2 = null;
array3 = new byte[1 * 1024 * 1024];//触发YGC Y 1MB O 1MB 2
array3 = null; byte[] array5 = new byte[1 * 1024 * 1024];// Y 2MB O 1MB
array1 = new byte[1 * 1024 * 1024];// Y 3MB
array1 = null;
array2 = new byte[1 * 1024 * 1024];// Y 1MB O 2MB YGC 3 array2 = null;
array3 = new byte[1 * 1024 * 1024];//Y 2MB O 2MB
array3 = null;
byte[] array6 = new byte[1 * 1024 * 1024];//Y 3MB O 2MB
array1 = new byte[1 * 1024 * 1024];//Y 1MB O 3MB YGC 4 array1 = null;
array2 = new byte[1 * 1024 * 1024];//Y 2MB
array2 = null;
array3 = new byte[1 * 1024 * 1024];//Y 3MB
array3 = null;
byte[] array7 = new byte[1 * 1024 * 1024];//YGC 5 }
}

02.启动JVM参数

-XX:NewSize=5M -XX:MaxNewSize=5M -XX:InitialHeapSize=10M -XX:MaxHeapSize=10M -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=2M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

其中,参数-XX:PretenureSizeThreshold,参数要设置大对象阈值为2MB,也就是超过2MB,就直接进入老年代。

大对象大小是3MB。一旦对象大小超过3MB,不会进入新生代,直接进入老年代。

启动命令:

java  -jar -XX:NewSize=5M -XX:MaxNewSize=5M -XX:InitialHeapSize=10M -XX:MaxHeapSize=10M -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=2M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log jvm-demo.jar

03.GC日志

启动之后就得到如下GC日志:

老年代

年轻代

Java HotSpot(TM) 64-Bit Server VM (25.151-b12) for windows-amd64 JRE (1.8.0_151-b12), built on Sep  5 2017 19:33:46 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 16703268k(7221016k free), swap 23781156k(8613656k free)
CommandLine flags: -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:MaxNewSize=5242880 -XX:MaxTenuringThreshold=15 -XX:NewSize=5242880 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=2097152 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC
0.121: [GC (Allocation Failure) 0.121: [ParNew: 3155K->512K(4608K), 0.0041165 secs] 3155K->766K(9728K), 0.0042644 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.125: [GC (Allocation Failure) 0.125: [ParNew: 3663K->0K(4608K), 0.0016667 secs] 3917K->1732K(9728K), 0.0017448 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.127: [GC (Allocation Failure) 0.127: [ParNew: 3142K->0K(4608K), 0.0013221 secs] 4875K->2756K(9728K), 0.0013592 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.129: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2756K(5120K)] 4878K(9728K), 0.0004498 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.129: [CMS-concurrent-mark-start]
0.130: [GC (Allocation Failure) 0.130: [ParNew: 3146K->0K(4608K), 0.0005869 secs] 5902K->2756K(9728K), 0.0006362 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.131: [GC (Allocation Failure) 0.131: [ParNew: 3148K->0K(4608K), 0.0007974 secs] 5904K->3780K(9728K), 0.0008262 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 4608K, used 2207K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
eden space 4096K, 53% used [0x00000000ff600000, 0x00000000ff827f38, 0x00000000ffa00000)
from space 512K, 0% used [0x00000000ffa80000, 0x00000000ffa80000, 0x00000000ffb00000)
to space 512K, 0% used [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
concurrent mark-sweep generation total 5120K, used 3780K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 2976K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 330K, capacity 386K, committed 512K, reserved 1048576K

04.分析GC日志

(1).代码块1

先看如下代码:

  byte[] array1 = new byte[1 * 1024 * 1024];
array1 = null;
byte[] array2 = new byte[1 * 1024 * 1024];
array2 = null;
byte[] array3 = new byte[1 * 1024 * 1024];
array3 = null;
byte[] array4 = new byte[1 * 1024 * 1024];

这段代码直接分配了4个1MB的数组,并且在第4个数组的时候,会因为新生代内存不足触发YGC。

此时内存分配如下:

对应如下GC日志:

0.121: [GC (Allocation Failure) 0.121: [ParNew: 3155K->512K(4608K), 0.0041165 secs] 3155K->766K(9728K), 0.0042644 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此时,可以看到新生代就只剩512K的对象,这个奇怪的512KB的对象进入Survivor From区。

那么大小为1MB的数组对象去哪里呢?肯定不是这个奇怪的512KB的对象。

这1MB的数组首先肯定是准备进入Survivor From区,可是,在我们设置的JVM参数下,只有0.5MB,明显是不够分配的。根据JVM YoungGC的规则,Survivor区放不下GC之后存活的对象,直接进入老年代

所以,1MB的数组对象是直接进入到老年代了。

此时,内存分配如下:

(2).代码块2

紧接这就是这块代码:

 array1 = new byte[1 * 1024 * 1024];
array1 = null;
array2 = new byte[1 * 1024 * 1024];
array2 = null;
array3 = new byte[1 * 1024 * 1024];

这里再次创建了3个1MB的数组对象,并且会触发一次YoungGC;

对应 GC日志如下:

0.125: [GC (Allocation Failure) 0.125: [ParNew: 3663K->0K(4608K), 0.0016667 secs] 3917K->1732K(9728K), 0.0017448 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此时,Young GC之后,新生代变成0KB,那么存活的大小为1MB的数组对象去哪里呢?

这1MB的数组首先肯定是准备进入Survivor From区,可是,在我们设置的JVM参数下,只有0.5MB,明显是不够分配的。根据JVM YoungGC的规则,Survivor区放不下GC之后存活的对象,直接进入老年代

所以,1MB的数组对象是直接进入到老年代了。

之前看到的未知的对象512KB也进入到老年代,此时内存分配如下:

(3).代码块3
array3 = null;
byte[] array5 = new byte[1 * 1024 * 1024];
array1 = new byte[1 * 1024 * 1024];
array1 = null;
array2 = new byte[1 * 1024 * 1024];

这里再次创建了3个1MB的数组对象,并且会触发一次YoungGC;

对应的GC日志如下:

0.127: [GC (Allocation Failure) 0.127: [ParNew: 3142K->0K(4608K), 0.0013221 secs] 4875K->2756K(9728K), 0.0013592 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此时内存分配如下:

(4).代码块4
array2 = null;
array3 = new byte[1 * 1024 * 1024];//Y 2MB O 2MB
array3 = null;
byte[] array6 = new byte[1 * 1024 * 1024];
array1 = new byte[1 * 1024 * 1024];

这里再次创建了3个1MB的数组对象,并且会触发一次YoungGC;并且在这儿,触发Young GC之前触发了一次CMS的Old GC,触发的条件就是老年代可用空间小于了历次Young GC后升入老年代的对象的平均大小。此时新生代大小变成0KB

对应的GC日志如下:

0.129: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2756K(5120K)] 4878K(9728K), 0.0004498 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.129: [CMS-concurrent-mark-start]
0.130: [GC (Allocation Failure) 0.130: [ParNew: 3146K->0K(4608K), 0.0005869 secs] 5902K->2756K(9728K), 0.0006362 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此时内存分配如下:

(5).代码块5
array1 = null;
array2 = new byte[1 * 1024 * 1024];//Y 2MB
array2 = null;
array3 = new byte[1 * 1024 * 1024];//Y 3MB
array3 = null;
byte[] array7 = new byte[1 * 1024 * 1024];

此时,再创建3个1MB的数组对象,再次触发一次Young GC,执行完YoungGC,此时新生代大小变成0KB;

对应的GC日志如下:

0.131: [GC (Allocation Failure) 0.131: [ParNew: 3148K->0K(4608K), 0.0007974 secs] 5904K->3780K(9728K), 0.0008262 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此时内存分配如下:

(6).总结

如下GC堆内存日志我们也可以去验证下上面的推测:

此时新生代使用了53%的大小,我们还有一个1MB的数组,可能还存在一些未知对象。

在老年代中使用了大约3MB的空间,应该就是上图中的对象。

Heap
par new generation total 4608K, used 2207K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
eden space 4096K, 53% used [0x00000000ff600000, 0x00000000ff827f38, 0x00000000ffa00000)
from space 512K, 0% used [0x00000000ffa80000, 0x00000000ffa80000, 0x00000000ffb00000)
to space 512K, 0% used [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
concurrent mark-sweep generation total 5120K, used 3780K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 2976K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 330K, capacity 386K, committed 512K, reserved 1048576K

3.几个触发Full GC的条件

第一:是老年代可用内存小于新生代全部对象的大小,如果没开启空间担保参数,会直接触发Full GC,所以一般空间担保参数都会打开;注:jDK1.8之后已经取消了-XX:-HandlePromotionFailure 机制

第二:是老年代可用内存小于历次新生代GC后进入老年代的平均对象大小,此时会提前Full GC;

第三:是新生代Minor GC后的存活对象大于Survivor,那么就会进入老年代,此时老年代内存不足。

上述情况都会导致老年代Full GC。

第四:就是“-XX:CMSInitiatingOccupancyFaction”参数,

如果老年代可用内存大于历次新生代GC后进入老年代的对象平均大小,但是老年代已经使用的内存空间超过了这个参数指定的比例,也会自动触发Full GC。默认92%

结合代码和内存变化图一步步弄懂JVM的FullGC的更多相关文章

  1. 一文带你弄懂 JVM 三色标记算法!

    大家好,我是树哥. 最近和一个朋友聊天,他问了我 JVM 的三色标记算法.我脑袋一愣发现竟然完全不知道!于是我带着疑问去网上看了几天的资料,终于搞清楚啥事三色标记算法,它是用来干嘛的,以及它和 CMS ...

  2. 一步步弄懂HTTPS

    为什么要使用HTTPS? HTTP采用明文传输,存在被监听.内容被篡改.身份被冒充的可能.为了保证安全性,需要对数据进行加密,这便有了HTTPS. 一步步分析HTTPS 1. 采用哪种加密方式加密数据 ...

  3. 不是吧!做了两年java还没弄懂JVM堆?进来看看你就明白了

    堆的核心概述 一个JVM实例只存在一个堆内存,堆也是java内存管理的核心区域Java堆区在jvm启动的时候被创建,其空间大小也就确定了.是jvm管理的最大一块内存空间.(堆内存的大小可以调节)< ...

  4. 阿里P8Java大牛仅用46张图让你弄懂JVM的体系结构与GC调优。

    本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述.图文并茂不生枯燥. 此PPT长达46页,全部展示篇幅过长,本文优先分享前十六页 ...

  5. 46张PPT弄懂JVM、GC算法和性能调优!

    来源:cnblogs.com/cyfonly/p/5807121.html 本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述. ...

  6. Golang, 以 9 个简短代码片段,弄懂 defer 的使用特点

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  7. Pythontutor:可视化代码在内存的执行过程

    http://www.pythontutor.com/visualize.html今天去问开发一个Python浅拷贝的问题,开发给了一个神器,可以可视化代码在内存的执行过程,一看即懂,太NB了!~真是 ...

  8. 解密native代码的内存使用

    前言 无论是从资源使用的角度,还是从发现内存泄漏问题的角度来看,在性能测试或者系统的稳定性测试中,内存的使用情况是一个很重要的监控点.为保证项目的质量前移,输入法内核测试小组的同学分配到了一个新的任务 ...

  9. c++ 汇编代码看内存分配

    汇编代码看内存分配 (1). 程序运行时分为存储区域分为 存储区域 存储内容 extra 代码区 存放代码指令,包括除字符串常量的字面值 静态存储区 存放静态变量和全局变量 执行main之前就分配好了 ...

  10. 必须弄懂的495个C语言问题

    1.1 我如何决定使用那种整数类型? 如果需要大数 值(大于32, 767 或小于¡32, 767), 使用long 型.否则, 如果空间很重要(如有大数组或很多结构), 使用short 型.除此之外 ...

随机推荐

  1. MongoDB数据库与Python的交互

    一.缘由 这是之前学习的时候写下的基础代码,包含着MongDB数据库和Python交互的基本操作. 二.代码实现 import pymongo #连接数据库 client=pymongo.MongoC ...

  2. tcp/udp 协议特性和三次握手

    一.TCP/UDP协议特性1)TCP特性:工作在传输层.建立连接.可靠的.错误检查 2)UDP特性:工作在传输层.不需要连接.不可靠的.有限的错误检查.传输性能高 2.控制位及确认号解释 控制位:由6 ...

  3. 为什么总是应该考虑给定 List 的初始大小

    在 .Net 技术中,使用 List<> 来存储数据是很常见的.List<> 是一个可以动态增长的泛型集合类型,可以存储任何类型的数据. 但是,在实际使用中,很多人并不注意给定 ...

  4. Redis——02 学习

    Redis--02 前面了解了 Redis 以及在 Linux 的安装,下面了解一些 Redis 常用的命令. Redis 常用命令: Redis 是 Key-Value 形式,Key 为字符串类型, ...

  5. django.db.migrations.exceptions.NodeNotFoundError: Migration apitest.0001_initial dependencies reference nonexistent parent node ('product', '0001_initial')

    执行python manage.py makemigrations时出现以下错误 D:\autotestplat>python manage.py makemigrations Tracebac ...

  6. week_5

    Andrew Ng机器学习笔记---by Orangestar Week_5 重点:反向传播算法,backpropagation 1. Cost Function神经元的代价函数 回顾定义:(上节回顾 ...

  7. 第一章 --------------------WPF基础概述

    1.在使用WPF之前我一直在思考为什么要使用WPF? 主要原因在于我已经受够了MFC和Winform 和QT的界面设计.尤其是MFC的界面设计,使用一个界面库十分的复杂,并且我的绝大多数时间都是用在这 ...

  8. 浅谈promise对象

    背景: 最近项目在做小程序的开发,涉及设计一个统一的登录公共方法,当实现时涉及到多个异步请求,那么问题来了,如何让多个异步请求先后同步进行呢?很多人会想到使用多层嵌套套来实现,就像这样: functi ...

  9. 如何用 30s 给面试官讲清楚什么是 Token

    引言 前文介绍了 Session-Cookie 的认证过程,简单回顾下基本步骤: 客户端(浏览器)向服务器发送用户名和密码 服务器验证通过后,创建 Session 对象,在 Session 中保存该用 ...

  10. FFmpeg 解码内存泄漏汇总,sws_getContext函数无法释放问题

    使用FFmpeg库做的项目,调试项目的时候发现,连续解视频进行播放,会有明显内存增加问题.连续工作10个小时后就会 被linux 内核kill掉. 通过逐步注掉代码和网上查阅资料.最后发现内存泄漏有一 ...