现象

今天在启动tomcat的过程中遇到启动时间特别长的问题,经过分析发现new SecureRandom().engineGenerateSeed(16)执行时间特别慢。

分析

通过查询发现,jdk本身有一个bug:JDK-6521844 : SecureRandom hangs on Linux Systems。这个bug产生的原因在另一篇文章里有说明[1]:

Q: Why the SecureRandom generateSeed is so slow or even hang on Linux OS?
A: When you login, it hangs or takes more than a minute to get the response.
If your server is on a Linux OS, the culprit here is SecureRandom generateSeed()
which uses /dev/random to generate the seed. However, /dev/random is a blocking 
number generator and if it doesn't have enough random data to provide, it will 
simply wait until it does, which forces the JVM to wait. Keyboard and mouse input
as well as disk activity can generate the randomness or entropy needed. But on a
server that lacks such activies, the problem may arise.

To verify this, run command:

cat /proc/sys/kernel/random/entropy_avail

it could be 150 or less, instead of the normal 3000-4000.

There are two options to workaround this problem:

1) Use jvm param:  -Djava.security.egd=file:/dev/./urandom

or even better

2) run daemon: /sbin/rngd -r /dev/urandom -o /dev/random -t 5
you can add this in rc.local or you can use
/etc/init.d/rngd, but make sure /etc/sysconfig/rngd has
EXTRAOPTIONS="-r /dev/urandom -o /dev/random -t 5"

Or, if you are on systemd, then you might already have rngd.service,
just enable and start it.

run ps -aux|grep rngd to see it is running.

大概意思就是说在linux环境下,SecureRandom会用/dev/random来生成随机种子,而/dev/random是一种阻塞的数据生成器。如果没有足够的数据它会一直等待。这些数据是由键盘、鼠标、磁盘等硬件产生的。如果这些操作比较少,就会导致阻塞。一般服务器是不会连接键盘和鼠标外设的。

问题的解决方式:

  1. 增加jvm启动参数 -Djava.security.egd=file:/dev/./urandom dev/urandom(“unlocked”,非阻塞的随机数发生器)。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它产生的随机数的效果不如random

  2. 打开 $JAVA_PATH/jre/lib/security/java.security 这个文件,找到下面的内容:securerandom.source=file:/dev/random替换成:securerandom.source=file:/dev/./urandom

关于urandom的文章 可参考:myths-about-urandom

  • 强引用(StrongReference) 一般的赋值就是建立强引用,只要有强引用就不会被回收。
  • 软引用(Soft Reference) 在系统将要发生内存溢出时进行回收,如果回收后还是不够内存才跑出内存溢出异常。内存足够则不会回收。
  • 弱引用(Weak Reference) 只能存活到下一次GC时,不管内存是否足够。
  • 虚引用(Phantom Reference) 对对象的生存时间没影响,目的是为了在对象被回收时得到系统通知。

对于软引用、弱引用、虚引用 可以创建一个referenceQueue来跟踪对象是否被回收。

级别 回收时间 用途 生存时间
强引用 从来不会 对象的一般状态 JVM停止运行时终止
软引用 在内存不足时(fullgc) 缓存 内存不足时终止
弱引用 在垃圾回收时 缓存 gc运行后终止
虚引用 Unknown Unknown Unknown