今天我做了个实验报错了,是把数据从一方传到远程的kafka里。发送方启动server.jar,kafka的接收方也启动server.jar。
java -jar server-v1.0.jar -d C:\Users\Administrator\Desktop\test\ -s 172.21.6.57 -t taxi
传输了不久之后,报错了。
报错日志如下:
1 | # |
原因:典型的内存错误 。发生了FullGC,老年代不够用了。那么应该如果调整JVM参数,不让他报错呢?光增大老年代大小就可以了吗?万一内存就这么大怎么办?
按说不应该用这么多内存啊,是不是发生了内存泄漏?怎么查看这个代码存不存在内存泄漏的情况?
我们都知道发生内存泄漏的情况会很多,比如
(1)静态集合类,例如HashMap和Vector。如果这些容器为静态的,由于它们的声明周期与程序一致,那么容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。
(2)各种连接,例如数据库的连接、网络连接以及IO连接等。
(3)监听器。在Java语言中,往往会使用到监听器。通常一个应用中会用到多个监听器,但在释放对象的同时往往没有相应的删除监听器,这也可能导致内存泄漏。
(4)变量不合理的作用域。一般而言,如果一个变量定义的作用域大于其使用范围,很有可能会造成内存泄漏,另一方面如果没有及时地把对象设置为Null,很有可能会导致内存泄漏的放生,如下:1
2
3
4
5
6
7
8
9
10
11class Server{
private String msg;
public void receiveMsg(){
readFromNet();//从网络接收数据保存在msg中
saveDB()//把msg保存到数据库中
}
从上面的代码中,通过readFromNet()方法接收的消息保存在变量msg中,然后调用saveDB()方法把msg的内容保存到数据库中,此时msg已经没用了,但是由于msg的声明周期与对象的声明周期相同,此时msg还不能被回收,因此造成了内存泄漏。对于这个问题,有如下两种解决方案:第一种方法,由于msg的作用范围只在receiveMsg()方法内,因此可以把msg定义为这个方法的局部变量,当方法结束后,msg的声明周期就会结束,此时垃圾回收器就会可以回收msg的内容了;第二种方法,在使用完msg后就设置为null,这样垃圾回收器也会自动回收msg内容所占用的内存空间。
(5)单例模式可能会造成内存泄漏
利用工具进行检查
利用jconsole发现(直接在cmd里输入jconsole就能出来),老年代确实用满了,大概是1.6G的样子,名称 = ‘PS MarkSweep’, 收集 = 73, 总花费时间 = 5 分钟,可以发现光老年代的GC时间达到了5分钟,这不行啊。总共运行7分钟程序,光GC时间就5分钟。然后我调大了老年代大小,设置成了堆大小是4G,新生代是400m。但是运行了一段时间后,内存占用又上升到了3.2G左右,这个这么占内存吗?
利用jvisualvm.exe 可以查看JVM参数,但是不知道为啥这个程序看不见。。。显示出来一个 - 就。通过java命令行加入 JVM参数后,就显示出来了。
通过这个工具发现了有一些对象,年代数特别大,58.怎么回事呢?还有一个疑问,当类加载数直线上升时,代表了啥?(类加载数从1000到5000)
会不会和ByteBuffer有关
因为在代码中用到了ByteBuffer,所以怀疑是不是和这个有关。
代码中有一段是这样的
1 | randomAccessFile = new RandomAccessFile(f, "r"); |
我们都知道写NIO程序经常使用ByteBuffer来读取或者写入数据,那么使用ByteBuffer.allocate(capability)还是使用ByteBuffer.allocteDirect(capability)来分配缓存了?第一种方式是分配JVM堆内存,属于GC管辖范围,由于需要拷贝所以速度相对较慢;第二种方式是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快,但是内存分配起来比较慢,所以并非不论什么时候allocateDirect的操作效率都是最高的。但是当操作数据量非常小时,两种分配方式操作使用时间基本是同样的(大小是1024000之前都差不多),第一种方式有时可能会更快,可是当数据量非常大时,用ByteBuffer.allocteDirect(capability)这个就比较快了。
无意收获
今天读了一篇博客,http://www.ityouknow.com/arch/2017/02/12/a-script-caused-bloody-case.html.其中,有这个一段,“重新启动观察之后mimor gc的频率确实有所下降,测试大约过了3小时候之后又反馈tomcat down掉了,继续分析启动参数配置的时候发现了这么一句-XX:-+DisableExplicitGC,显示的禁止了System.gc(),但是使用了java.nio的大量框架中使用System.gc()来执行gc期通过full gc来强迫已经无用的DirectByteBuffer对象释放掉它们关联的native memory,如果禁用会导致OOM,随即怀疑是否是这个参数引发的问题,在启动参数中去掉它。”
大意是我没有禁用,所以不能GC,所以OOM。明白了。。。。。如果我们的应用中使用了java nio中的direct memory,那么使用-XX:+DisableExplicitGC一定要小心,存在潜在的内存泄露风险。
但是这个程序的内存跑到后期的时候,占用的内存还是很大,老年代GC时间很长,虽然没有OOM了,但是这样也不行啊。。。
ByteBuffer操作介绍
https://www.cnblogs.com/jiduoduo/p/6397454.html
小插曲
因为这个程序占用了CPU 几乎100%,造成了虚拟机抱了这些1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21[root@rabbitmq _posts]#
Message from syslogd@rabbitmq at May 25 20:39:18 ...
kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 33s! [rtkit-daemon:736]
Message from syslogd@rabbitmq at May 25 20:39:39 ...
kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 35s! [scsi_eh_1:275]
Message from syslogd@rabbitmq at May 25 20:39:39 ...
kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 21s! [pickup:4845]
Message from syslogd@rabbitmq at May 25 20:39:39 ...
kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 22s! [dhclient:871]
Message from syslogd@rabbitmq at May 25 20:39:39 ...
kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 22s! [master:2807]
Message from syslogd@rabbitmq at May 25 20:39:39 ...
kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 29s! [abrt-hook-ccpp:5402]
Message from syslogd@rabbitmq at May 25 20:39:58 ...
kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 21s! [abrt-hook-ccpp:5402]
原因是占用过多CPU,造成内核软死锁(soft lockup)。Soft lockup名称解释:所谓,soft lockup就是说,这个bug没有让系统彻底死机,但是若干个进程(或者kernel thread)被锁死在了某个状态(一般在内核区域),很多情况下这个是由于内核锁的使用的问题。
知识点
- Code Cache (non-heap):HotSpot Java虚拟机包括一个用于编译和保存本地代码(native code)的内存,叫做“代码缓存区”(code cache) (这里大概是6、7M的大小)
你会写导致内存泄漏的代码吗?
https://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java
参考
https://blog.csdn.net/jsqfengbao/article/details/44787267
https://www.cnblogs.com/mengfanrong/p/3984841.html