偶遇 JDK 1.8 還未修復的 SecureRandom.getInstance("SHA1PRNG") 之 bug

樓主今天興高采烈的在部署環境,下載 JDK,打包項目,上傳至服務器。java

配置 JDK ,打包上傳項目樓主就不在這裏重複了,讀者自行解決哈!程序員

 

1. 啓動項目數據庫

java -jar xxxx.jar vim

令樓主沒有想到的是:程序卡主了,卡在了數據庫創建鏈接的位置。(查看方法方式: jstack <pid> 便可)bash

 

2. 堆棧信息服務器

因爲是項目剛一啓動,初始化數據庫鏈接池,並無太多的線程堆棧。這裏我貼一下我遇到的主要問題的堆棧信息:oracle

"restartedMain" #11 prio=5 os_prio=0 tid=0x00007f4430002800 nid=0x65b0 runnable [0x00007f447837a000]
   java.lang.Thread.State: RUNNABLE
	at java.io.FileInputStream.readBytes(Native Method)
	at java.io.FileInputStream.read(FileInputStream.java:255)
	at sun.security.provider.SeedGenerator$URLSeedGenerator.getSeedBytes(SeedGenerator.java:539)
	at sun.security.provider.SeedGenerator.generateSeed(SeedGenerator.java:144)
	at sun.security.provider.SecureRandom$SeederHolder.<clinit>(SecureRandom.java:203)
	at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:221)
	- locked <0x00000000d6eb8930> (a sun.security.provider.SecureRandom)
	at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
	at oracle.security.o5logon.O5Logon.a(Unknown Source)
	at oracle.security.o5logon.O5Logon.<clinit>(Unknown Source)
	at oracle.jdbc.driver.T4CTTIoauthenticate.<init>(T4CTTIoauthenticate.java:566)
	at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:370)
	at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:546)
	at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:236)
	at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
	at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:521)

或許你會問我,爲何你要定位到這個線程?主要是由於道友通過好幾回 jstack 操做,發現該線程一直都停留在該位置,這就足以說明這個線程是當前線程!dom

 

3. 分析堆棧信息jvm

從上面的堆棧信息中,咱們能夠得知那些信息?ide

  • 當前線程名稱爲 : restartedMain
  • 當前線程狀態爲: RUNNABLE
  • 當前線程 ID: 0x00007f4430002800
  • native 線程 ID: 0x65b0
  • 當前運行位置 : at java.io.FileInputStream.readBytes(Native Method)

經過分析堆棧,想必讀取文件不會有什麼問題,咱們往線程堆棧的上層追蹤。目標點落在 

    at sun.security.provider.SeedGenerator$URLSeedGenerator.getSeedBytes(SeedGenerator.java:539)

很明顯,這裏是  JDK 源碼的調用,接下來打卡 jdk 源碼進行查看!

 

 

4. 源碼分析

咱們經過堆棧信息一步步跟進源碼查看,

從方法  nextBytes 上來看,他調用這個是爲了:生成用戶指定的隨機字節數。好吧,那就是它在生成的時候,須要讀取某個文件來生成隨機數。

 

5. 寫個測試驗證想法

[ryan@ryanmacbook ~]$ vim HelloWorld.java 
import java.security.SecureRandom;

class HelloWorld {

    public static void main(String args[]) throws Exception {

        byte[] bytes = new byte[32];

        SecureRandom.getInstance("SHA1PRNG").nextBytes(bytes);

        System.out.println("SourceRandom nextBytes : " + new String(bytes));
    }
}

緊接着執行

javac HelloWorld.java

這個要是讀者你不會,那你就放棄java吧,java 界可能不適合你。

接下來執行 

java HelloWorld

執行後,,的結果是 

[ryan@ryanmacbook ~]$ java HelloWorld


^C
[ryan@ryanmacbook ~]$

卡住了,樓主強制 kill 了。

 

說明咱們的問題點就是這裏。

 

接下來,解決辦法是啥?你要問樓主樓主也不知道,咋辦?上百度唄,百度都不會用的程序員不是好程序員:

 

6. 解決辦法

最後的解決辦法就是 ,在項目啓動的時候,修改 java.security.egd,經過以下方式,專業術語叫作    熵池策略   

java -Djava.security.egd=file:/dev/urandom HelloWorld

問題的本質仍是 Oracle 驅動類中調用了 SecureRandom.nextBytes() 。樓主還沒來得及看其餘驅動程序是否也有這個問題,固然也不排除與操做系統有關係。待驗證其餘操做系統後,再補充!

 

果斷成功!

 

道友查了下,說是 JVM 的 bug , 在收集噪點的時候沒有收集全致使的。若是你的也有遇到這樣的問題,時而可用,時而不可用,那就是噪點收集的問題。

 

[參考連接] http://ifeve.com/jvm-random-and-entropy-source/

相關文章
相關標籤/搜索