记一次在使用javax.mail-1.5.2.jar包,做邮箱服务。由于Finalizable,导致的内存溢出问题的分析和排查处理过程。
异常现象分析
通过上面的内容可以看到,是应为Finalizer的问题引起的内容泄露,Finalizable对象的生命周期和普通对象的行为是完全不同的,列举如下:
1、JVM创建Finalizable对象
2、JVM创建 java.lang.ref.Finalizer实例,指向刚创建的对象。
3、java.lang.ref.Finalizer类持有新创建的java.lang.ref.Finalizer的实例。这使得下一次新生代GC无法回收这些对象。
4、新生代GC无法清空Eden区,因此会将这些对象移到Survivor区或者老生代。
5、垃圾回收器发现这些对象实现了finalize()方法。因为会把它们添加到java.lang.ref.Finalizer.ReferenceQueue队列中。
6、Finalizer线程会处理这个队列,将里面的对象逐个弹出,并调用它们的finalize()方法。
finalize()方法调用完后,Finalizer线程会将引用从Finalizer类中去掉,因此在下一轮GC中,这些对象就可以被回收了。
7、Finalizer线程会和我们的主线程进行竞争,不过由于它的优先级较低,获取到的CPU时间较少,因此它永远也赶不上主线程的步伐。
程序消耗了所有的可用资源,最后抛出OutOfMemoryError异常。
可通过如下简单的demo进行验证:
package com.onecloud.mail.test;
import java.util.concurrent.atomic.AtomicInteger;
class Finalizable {
static AtomicInteger aliveCount = new AtomicInteger(0);
Finalizable() {
aliveCount.incrementAndGet();
}
@Override
protected void finalize() throws Throwable {
Finalizable.aliveCount.decrementAndGet();
}
public static void main(String args[]) {
Finalizable f;
for (int i = 0; ; i++) {
f = new Finalizable();
if ((i % 1000000) == 0) {
System.out.format("After creating %d objects, %d are still alive.%n", new Object[]{i, Finalizable.aliveCount.get()});
}
}
}
}
代码分析
出现问题的类和方法为:AtallMailServiceImpl->receivePOP3Mail,发现其中用到的对象POP3Folder,实现了Object的finalize 方法,
/**
* Close the folder when we're finalized.
*/
protected void finalize() throws Throwable {
super.finalize();
close(false);
}
通过对finalize的原理分析,其出现的问题和此处有关系。
通过深入的查看代码,在POP3Folder存在POP3Message的引用,我们应该在使用POP3Folder对象后,释放对象的占用资源。
解决方案
对应的代码块都在 “AtallMailServiceImpl”类中receivePOP3Mail方法中
1、调用POP3Folder释放资源的方法:
pOP3Folder.close(true);
2、需要调用message的方法,取消缓存
mimeMessage.invalidate(true);
3、先判断邮件是否已收取,然后在读取邮件内容,
使用 String id = inbox2.getUID(mimeMessage);
替换原来的:String id = pmm.getMessageId();
4、去掉以上预加载的代码,此处会加载很多我们不会用到的信息,占用大量内存。
FetchProfile profile = new FetchProfile();
profile.add(FetchProfile.Item.ENVELOPE); // 获取信封
inbox.fetch(message, profile); // 预获取信息
5、 这里最好做下缓存的处理,避免频繁的查询数据库操作
boolean b = mailDAO.existsById(id);
6、建议调整线程池队列的大小,不要太多的线程同时运行。
以上仅仅是解决了线上环境报内存溢出的问题,关于邮箱服务的双高问题,还有如下的点:
内存和CPU的占用主要体现在如下两点:
- 用户附件的下载导致的IO问题。(线程过多时会急速升高),
- 邮件内容的过大导致的mysql查询和存储访问问题
后续如果需要对邮箱进行架构调整,需要结合业务分析是否不用收取附件或则采用NIO的方式异步解析附件的文件流。
top - 10:43:51 up 108 days, 17:19, 4 users, load average: 8.93, 8.96, 7.19
Tasks: 173 total, 3 running, 170 sleeping, 0 stopped, 0 zombie
%Cpu(s): 26.5 us, 67.8 sy, 0.0 ni, 4.6 id, 0.3 wa, 0.0 hi, 0.7 si, 0.1 st
KiB Mem : 7747768 total, 119608 free, 4869892 used, 2758268 buff/cache
KiB Swap: 7340028 total, 5577240 free, 1762788 used. 2523152 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5944 clouder 20 0 4754948 1.351g 7680 S 378.1 18.3 106:38.69 /usr/java/jdk1.8.0_212-amd64/bin/java -Xms1g -Xmx1g -Xmn512m -jar xiaoHuiMail.jar
2251 clouder 20 0 135016 2172 928 S 0.3 0.0 216:59.53 ./src/redis-server 127.0.0.1:6380
2289 clouder 20 0 5626900 201660 6164 S 0.3 2.6 272:20.99 /usr/java/jdk1.8.0_212-amd64/jre/bin/java -Djava.util.logging.config.file=/home/cl+
27161 clouder 20 0 5847724 627228 7280 S 0.3 8.1 126:08.56 /usr/java/jdk1.8.0_212-amd64/jre/bin/java -jar SMART_SPACE_CONTROLLER.jar
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
10 0 1755072 119712 0 2757704 0 0 0 512 6890 2062 32 67 1 0 0
9 1 1755068 117648 0 2759916 0 0 0 890 7715 2272 31 67 1 0 0
13 0 1755664 183672 0 2693284 0 600 0 11895 12382 2163 30 69 1 0 0
8 0 1755664 180064 0 2697052 0 0 0 521 7373 1490 28 71 0 0 0
4 0 1755656 176912 0 2699956 32 0 32 1088 8625 2345 31 68 1 0 0
9 0 1755644 173304 0 2703052 32 0 32 1549 10972 4810 32 60 7 1 0
11 1 1755632 164264 0 2711412 0 0 1952 16085 10492 5629 32 54 11 3 0
3 0 1755624 160508 0 2714640 0 0 1060 16090 6450 3072 29 47 20 4 0
11 0 1755620 159428 0 2716592 0 0 16 9563 14123 5609 35 47 17 1 0
9 2 1755616 154548 0 2720568 0 0 0 5028 7173 2098 32 67 1 0 0
11 0 1755608 150524 0 2723660 64 0 64 1438 15739 6397 30 59 8 2 0
8 0 1755604 146396 0 2727640 0 0 0 4399 17011 8086 30 57 11 2 0
8 0 1755600 143016 0 2731056 0 0 0 3584 8472 2338 30 68 1 0 0
10 0 1755596 141252 0 2732520 0 0 0 534 6632 1935 32 68 0 0 0
8 0 1755596 137280 0 2736424 0 0 0 2239 7827 2768 30 69 0 0 0
10 0 1755592 134452 0 2739080 0 0 8 411 13234 3465 30 63 6 1 0
10 1 1755592 131344 0 2741968 0 0 0 3906 6330 1168 27 73 0 0 0
10 0 1755592 128776 0 2744492 0 0 0 7301 6829 1976 29 71 0 0 0
7 1 1755592 125300 0 2748200 0 0 0 783 6578 1994 28 71 0 0 0
9 1 1755592 121952 0 2751560 0 0 0 2354 7009 2496 30 70 0 0 0
11 0 1755588 126604 0 2754096 0 0 0 880 17984 7569 32 54 13 2 0
10 0 1755584 125748 0 2755360 0 0 8 10881 16753 4258 37 58 3 2 0
avg-cpu: %user %nice %system %iowait %steal %idle
25.50 0.00 74.50 0.00 0.00 0.00
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
xvdb 0.00 1.00 0.00 181.00 0.00 5173.00 57.16 0.34 1.88 0.00 1.88 0.24 4.40
dm-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
avg-cpu: %user %nice %system %iowait %steal %idle
26.25 0.00 73.75 0.00 0.00 0.00
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvda 0.00 1.00 0.00 5.00 0.00 36.00 14.40 0.00 0.60 0.00 0.60 0.20 0.10
xvdb 0.00 0.00 0.00 132.00 0.00 3625.50 54.93 0.21 1.60 0.00 1.60 0.23 3.00
dm-0 0.00 0.00 0.00 6.00 0.00 36.00 12.00 0.00 0.67 0.00 0.67 0.17 0.10
dm-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
avg-cpu: %user %nice %system %iowait %steal %idle
33.92 0.00 66.08 0.00 0.00 0.00
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
xvdb 0.00 2.00 0.00 800.00 0.00 34402.50 86.01 18.90 23.62 0.00 23.62 0.20 16.30
dm-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
调整后的GC信息
伙伴们,如果有遇到内存溢出的相关问题,都可以给我留言,另外关于邮箱服务的优化,如果有想法的小伙伴,也可以给我留言。