簡體   English   中英

如何在我的 Java 應用程序中設置 JVM 時間(不是操作系統時間)?

[英]How can I set JVM time(not OS time) in my Java application?

我正在設計一個回測應用程序,它將重放以前記錄的數據並處理數據。 我想在我的應用程序中配置不會影響操作系統的日期和時間值。 我發現有一個用於設置 JVM 時區的 JVM 參數,但我找不到類似的日期和時間。 有可能這樣做嗎?

問候

更改特定進程的日期/時間的常用方法是使用LD_PRELOAD 技巧掛鈎相應的系統函數。 Linux 中有兩個基本函數來獲取絕對時間:gettimeofdayclock_gettime 這是一個攔截這兩個函數的示例庫

不幸的是,上述技巧僅適用於 Linux。 但這是另一個專門針對 Java 的便攜式解決方案。 它依賴於JVM 工具接口

在 OpenJDK 中,所有用於獲取當前日期/時間的 Java API 最終都會調用System.currentTimeMillis()VM.getNanoTimeAdjustment() 后者是JDK內部提供高分辨率Clock 因此,為了更改 Java 應用程序的日期/時間,調整這兩種方法的實現就足夠了。

思路如下。

  • JVM TI 代理通過訂閱NativeMethodBind事件來攔截本地方法綁定。
  • 當為currentTimeMillisgetNanoTimeAdjustment調用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對象並使用它。

因此,這是你必須做的:

  1. 查找代碼中所有硬編碼以使用操作系統時鍾的位置。 System.currentTimeMillis()new Date()X.now()其中 X 是java.time包中的任何類型( InstantLocalDateTime等),關鍵是任何在java.time執行此操作的庫。 將它們替換為適當的基於時鍾的調用。 System.currentTimeMillis()變為clock.millis() Instant.now()變為clock.instant() ,依此類推。

  2. 在測試期間,制作您自己的時鍾,或使用Clock.fixed來獲取始終報告完全相同時間的時鍾。 在生產期間,時鍾必須是Clock.systemUTC()

這可能需要使用依賴注入框架。 或者,為您的整個應用程序創建一個全局時鍾(一個具有靜態時鍾字段的類,所有代碼都從中讀取,並且您的測試代碼設置,默認為 systemUTC)。

據我所知,沒有辦法配置System.currentTimeMillis和朋友使用的時鍾。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM