[英]How can I set JVM time(not OS time) in my Java application?
我正在設計一個回測應用程序,它將重放以前記錄的數據並處理數據。 我想在我的應用程序中配置不會影響操作系統的日期和時間值。 我發現有一個用於設置 JVM 時區的 JVM 參數,但我找不到類似的日期和時間。 有可能這樣做嗎?
問候
更改特定進程的日期/時間的常用方法是使用LD_PRELOAD 技巧掛鈎相應的系統函數。 Linux 中有兩個基本函數來獲取絕對時間:gettimeofday
和clock_gettime
。 這是一個攔截這兩個函數的示例庫。
不幸的是,上述技巧僅適用於 Linux。 但這是另一個專門針對 Java 的便攜式解決方案。 它依賴於JVM 工具接口。
在 OpenJDK 中,所有用於獲取當前日期/時間的 Java API 最終都會調用System.currentTimeMillis()
或VM.getNanoTimeAdjustment()
。 后者是JDK內部提供高分辨率Clock
。 因此,為了更改 Java 應用程序的日期/時間,調整這兩種方法的實現就足夠了。
思路如下。
currentTimeMillis
或getNanoTimeAdjustment
調用NativeMethodBind
,代理會記住本機函數的地址並將其替換為自己的地址。此類代理的完整代碼在此處。
如何編譯:
g++ -O2 -fPIC -shared -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -olibfaketime.so faketime.cpp
如何運行:
java -agentpath:/path/to/libfaketime.so=<newtime> MainClass
其中newtime
是從 Epoch 開始的以毫秒為單位的絕對時間戳,或者(當以+
或-
開頭時)從當前時間開始的以毫秒為單位的相對偏移量。
唯一的問題是System.currentTimeMillis
是 JVM 內在的。 這意味着,JIT 編譯的方法可能會跳過調用 JNI 實現(從而跳過我們的鈎子)。 為了避免這種情況,我們可以簡單地使用更多 JVM 選項禁用相應的內在函數:
-XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_currentTimeMillis -XX:CompileCommand=dontinline,java.lang.System::currentTimeMillis
它不是。 沒有辦法讓例如System.currentTimeMillis()
(因此, Instant.now()
和其他此類調用)在不改變操作系統時鍾的情況下改變行為。
如果您想要“可測試”的時間,則不能調用這些方法。 相反,創建一個Clock
對象並使用它。
因此,這是你必須做的:
查找代碼中所有硬編碼以使用操作系統時鍾的位置。 System.currentTimeMillis()
、 new Date()
、 X.now()
其中 X 是java.time
包中的任何類型( Instant
、 LocalDateTime
等),關鍵是任何在java.time
執行此操作的庫。 將它們替換為適當的基於時鍾的調用。 System.currentTimeMillis()
變為clock.millis()
。 Instant.now()
變為clock.instant()
,依此類推。
在測試期間,制作您自己的時鍾,或使用Clock.fixed
來獲取始終報告完全相同時間的時鍾。 在生產期間,時鍾必須是Clock.systemUTC()
。
這可能需要使用依賴注入框架。 或者,為您的整個應用程序創建一個全局時鍾(一個具有靜態時鍾字段的類,所有代碼都從中讀取,並且您的測試代碼設置,默認為 systemUTC)。
據我所知,沒有辦法配置System.currentTimeMillis
和朋友使用的時鍾。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.