性能文章>【译】java.lang.System.getProperty() 的性能影响和解决方法>

【译】java.lang.System.getProperty() 的性能影响和解决方法转载

1年前
413438

‘java.lang.System.getProperty()’ 是 Java 开发人员用来读取在程序启动期间配置系统属性的通用 API。

当你将“-DappName=buggyApp”作为应用程序的启动JVM 参数传递时,可以通过调用“java.lang.System.getProperty()”来读取“appName”系统属性的值。

举个栗子:

public static String getAppName() {

   String app = System.getProperty("appName");
   return app;
}

调用上述方法时,将返回“buggyApp”。但是,如果在关键代码路径中使用“java.lang.System.getProperty()”,它可能会降低应用程序的性能。

在这篇文章中,让我们讨论调用“java.lang.System.getProperty()”对性能的影响,如何缓解它以及这个 API 触发的实际生产问题。

对性能的影响

‘java.lang.System.getProperty()’ API 底层使用 ‘java.util.Hashtable.get()’ API。请注意,‘java.util.Hashtable.get()’ 是一个同步 API。这意味着在任何给定时间只有一个线程可以调用 ‘java.util.Hashtable.get()’ 方法。

如果新线程在第一个线程仍在执行时尝试调用“java.util.Hashtable.get()”API,则新线程将处于阻塞状态。当线程处于 BLOCKED 状态时,它将无法继续前进。

只有当第一个线程完成执行 ‘java.util.Hashtable.get()’ API 时,新线程才能继续前进。因此,如果在关键代码路径中调用了“java.lang.System.getProperty()”或“java.util.Hashtable.get()”,则会影响事务的响应时间。

Atlassian SDK 中的实际问题

最近在 Atlassian SDK 中观察到了这种类型的退化。从此应用程序中捕获了线程转储,并使用线程转储分析工具fastThread进行了分析。

根据线程heapdump分析报告,有189个线程处于BLOCKED状态。下面是线程转储报告中的传递依赖图,显示了处于 BLOCKED 状态的线程的名称。当您单击图表中的线程名称时,该特定线程的堆栈跟踪将在工具中报告。

graph2.png
图:‘java.lang.System.getProperty()’ API 上的 189 个线程被阻塞,由fastThread报告

由于“Camel Thread #6 –boneThreadPool”(即图中的红色节点),所有这些线程都进入了 BLOCKED 状态。这是该线程堆栈跟踪的最初几行:

Camel Thread #6 – backboneThreadPool
Stack Trace is:
at java.util.Hashtable.get(Hashtable.java:362)
- locked <0x0000000080f5e118> (a java.util.Properties)
at java.util.Properties.getProperty(Properties.java:969)
at java.util.Properties.getProperty(Properties.java:988)
at java.lang.System.getProperty(System.java:756)
at net.java.ao.atlassian.ConverterUtils.enforceLength(ConverterUtils.java:16)
at net.java.ao.atlassian.ConverterUtils.checkLength(ConverterUtils.java:9)
:
:

图:获取 LOCK 的线程的堆栈跟踪

从堆栈跟踪中,您可以注意到该线程正在调用“java.lang.System.getProperty()”API。由于 ‘java.lang.System.getProperty()’ API 底层使用 ‘java.util.Hashtable.get()’ API(这是一个

是一个同步的 API 调用)。因此,“Camel Thread #6 –boneThreadPool”将是唯一允许进入此方法的线程。下面是最初的几行线程(共 189 个线程),它们处于 BLOCKED 状态,因为它们正在等待进入 ‘java.util.Hashtable.get()’ API。

http-nio-8080-exec-293 
Stack Trace is:
java.lang.Thread.State: BLOCKED (on object monitor)
at java.util.Hashtable.get(Hashtable.java:362)
- waiting to lock <0x0000000080f5e118> (a java.util.Properties)
at java.util.Properties.getProperty(Properties.java:969)
at java.util.Properties.getProperty(Properties.java:988)
at java.lang.System.getProperty(System.java:756)
at net.java.ao.atlassian.ConverterUtils.enforceLength(ConverterUtils.java:16)
at net.java.ao.atlassian.ConverterUtils.checkLength(ConverterUtils.java:9)
:
:

图:等待 LOCK 的 BLOCKED 线程之一的堆栈跟踪

http-nio-8080-exec-279 
Stack Trace is:
java.lang.Thread.State: BLOCKED (on object monitor)
at java.util.Hashtable.get(Hashtable.java:362)
- waiting to lock <0x0000000080f5e118> (a java.util.Properties)
at java.util.Properties.getProperty(Properties.java:969)
at java.util.Properties.getProperty(Properties.java:988)
at java.lang.System.getProperty(System.java:756)
at org.ofbiz.core.entity.EntityFindOptions.<init>(EntityFindOptions.java:124)
:
:

图:另一个等待 LOCK 的 BLOCKED 线程的堆栈跟踪

由于此“java.lang.System.getProperty()”API 存在于关键代码路径中,因此多个线程试图调用它。因此,所有试图调用此 API 的 189 个线程都被置于 BLOCKED 状态。最终,整体应用程序响应时间降低了。

解决办法是什么?

由于系统属性在运行时不会改变,我们不必在每个事务上都调用’java.lang.System.getProperty()’ API。相反,我们可以调用 ‘java.lang.System.getProperty()’ API 一次,缓存它的值,并在以后的所有调用中返回缓存的值,如下面的代码片段所示。

private static String app = System.getProperty("appName");
  
public static String getAppName() {
  
  return app;
}

如果你注意到上面的代码,’‘java.lang.System.getProperty()’ 现在被分配给一个静态成员变量。这意味着该 API 将在应用程序启动期间被调用,也只调用一次。从那时起,如果有人调用 getAppName() API,他将被返回缓存值。因此,应用程序线程不会在运行时进入 BLOCKED 状态。这个简单的更改可以提高应用程序的整体响应时间。

点赞收藏
金色梦想

终身学习。

请先登录,查看3条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

实现定时任务的六种策略

实现定时任务的六种策略

浅析AbstractQueuedSynchronizer

浅析AbstractQueuedSynchronizer

8
3