簡體   English   中英

Java:Getter和setter比直接訪問更快?

[英]Java: Getter and setter faster than direct access?

我測試了我在Linux上網本上使用VisualVM 1.3.7編寫的Java光線跟蹤器的性能。 我用剖面儀測量。
為了好玩,我測試了使用getter和setter以及直接訪問字段之間的區別。 getter和setter是標准代碼,沒有添加。

我沒想到會有任何分歧。 但直接訪問代碼的速度較慢。

這是我在Vector3D中測試的樣本:

public float dot(Vector3D other) {
    return x * other.x + y * other.y + z * other.z;
}

時間:1542毫秒/ 1,000,000次調用

public float dot(Vector3D other) {
    return getX() * other.getX() + getY() * other.getY() + getZ() * other.getZ();
}

時間:1453毫秒/ 1,000,000次調用

我沒有在微基准測試中測試它,而是在光線跟蹤器中測試。 我測試代碼的方式:

  • 我用第一個代碼啟動了程序並進行了設置。 光線跟蹤器尚未運行。
  • 我啟動了探查器並在初始化完成后等了一會兒。
  • 我開始了一個光線追蹤器。
  • 當VisualVM顯示足夠的調用時,我停止了探查器並等了一會兒。
  • 我關閉了光線追蹤程序。
  • 我用第二個代碼替換了第二個代碼,並在編譯后重復上述步驟。

我至少為這兩個代碼運行了20,000,000次調用。 我關閉了任何我不需要的程序。 我設置CPU的性能,所以我的CPU時鍾是最大的。 每時每刻。
第二個代碼如何快6%?

我做了一些微型基准測試,大量的JVM熱身,發現這兩種方法的執行時間完全相同

發生這種情況是因為JIT編譯器在getter方法中嵌入了對字段的直接訪問,從而使它們成為相同的字節碼。

謝謝大家幫我回答這個問題。 最后,我找到了答案。

首先, 波希米亞是正確的 :使用PrintAssembly我檢查了生成的匯編代碼是相同的假設。 是的,雖然字節碼不同,但生成的代碼是相同的。
所以masterxilo是對的 :探查器必須是罪魁禍首。 但是masterxilo對時序柵欄和更多儀器代碼的猜測並不屬實; 兩個代碼最終都是相同的。

所以還有一個問題:第二個代碼在Profiler中的速度有多快6%?

答案取決於VisualVM如何測量: 在開始分析之前,您需要校准數據。 這用於消除分析器導致的開銷時間。
雖然校准數據是正確的,但測量的最終計算不是。 VisualVM在字節碼中看到方法調用。 但它沒有看到JIT編譯器在優化時刪除了這些調用。
因此它消除了不存在的開銷時間。 這就是差異的出現方式。

如果您沒有參加統計學課程,無論編寫的程度如何,程序性能總會出現差異。 這兩種方法似乎以大致相同的速率運行的原因是因為訪問器字段只做一件事:它們返回一個特定的字段。 因為在存取方法中沒有其他任何事情發生,所以這兩種策略幾乎都做同樣的事情; 但是,如果您不知道封裝,即程序員隱藏用戶數據(字段或屬性)的程度,封裝的主要規則就是不向用戶顯示內部數據 將字段修改為公共意味着任何其他類都可以訪問這些字段,這對用戶來說可能非常危險。 這就是為什么我總是建議Java程序員使用訪問器和mutator方法,這樣字段就不會落入壞人之手。

如果您對如何訪問私有字段感到好奇,可以使用反射,實際訪問特定類的數據,以便在必須這樣做時可以改變它。 作為一個無聊的例子,假設您知道java.lang.String類包含char []類型的私有字段(即char數組)。 它對用戶隱藏,因此您無法直接訪問該字段。 (順便說一下,方法java.lang.String.toCharArray()為你訪問該字段。)如果你想連續訪問每個字符並將每個字符存儲到一個集合中(為了簡單起見,為什么不是java。 util.List?),這里是如何在這種情況下使用反射:

/**
    This method iterates through each character in a <code>String</code> and places each of them into a <code>java.util.List</code> of type <code>Character</code>.
    @param str The <code>String</code> to extract from.
    @param list The list to store each character into. (This is necessary because the compiler knows not which <code>List</code> to use, so it will automatically clear the list anyway.)
*/
public static void extractStringData(String str, List<Character> list) throws IllegalAccessException, NoSuchFieldException
{
    java.lang.reflect.Field value = String.class.getDeclaredField("value");
    value.setAccessible(true);
    char[] data = (char[]) value.get(str);
    for(char ch : data) list.add(ch);
}

作為旁注,請注意反射會從您的程序中獲得很多性能。 如果有一個字段,方法,內部或嵌套類,你必須以任何理由訪問它(這種情況極不可能),那么你應該使用反射。 反射消除寶貴表現的主要原因是它拋出了相對無數的例外。 我很高興能幫到你!

暫無
暫無

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

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