`
zkp664ej
  • 浏览: 13613 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

使用类共享提高性能

 
阅读更多

使用类共享提高性能
2010年12月09日
  共享类基础结构是在 IBM JRE for the Java platform SE 的版本 5 中首次引入的。最新版本对这个特性的改进有助于提高 Java 应用程序在启动时间和内存使用量方面的性能。在本文中,我们介绍这些改进并使用 Eclipse 和 Apache Tomcat 作为客户端和服务器端操作环境的示例,从而展示这些改进的好处。我们将提供安装说明,让您可以自己实践一下,但是您应该熟悉这两个应用程序以及 IBM 的类共享特性。如果您还不熟悉 IBM 的类共享特性,建议先阅读文章 "Java 技术,IBM 风格: 类共享",这篇文章解释了基本概念。
  如果希望实践本文提供的示例,现在可以下载 IBM JRE for Java 6 for Linux?? and AIX?? 的实现。目前没有可以单独下载的 Windows?? 实现,而是以 预构建的 Eclipse 下载包 形式提供这个实现。注意,需要 IBM 注册(免费)。
  IBM 共享类中的新特性?
  IBM JRE for Java 5 允许通过缓存在 JVM 之间共享类。在 IBM JRE for Java 6 中,可以使这个缓存持久化并用它共享编译的代码。另外,存储这些缓存项的方法效率更高了。
  共享的类
  在 Java 虚拟机 (JVM) 之间共享类的功能是在 IBM JRE for the Java platform SE 的版本 5 中首次引入的,在 Java 6 中继续支持并进一步增强了此功能。当 JVM 装载类时,可以把它们放在缓存中。当以后请求这个类时,会尽可能通过缓存满足请求,而不必从对应的 JAR 文件再次装载这个类。
  可以使用清单 1 中的命令行选项控制缓存的最大大小,但是请注意,这个最大大小可能受到操作系统共享内存限制的约束: Ahead of Time (AOT) 代码存储
  JVM 通常在执行程序时把 Java 方法编译为原生代码。在每次运行程序时,都会生成原生代码。IBM JRE for Java 6 SR1 JVM 引入了使用 Ahead of Time 编译技术编译 Java 方法的功能。用这种技术生成的原生代码不但可以在当前的 JVM 中使用,而且可以存储在共享类缓存中。使用共享类缓存启动的另一个 JVM 可以使用缓存中存储的 AOT 代码,从而减少启动时间。这是由于节省了编译所需的时间,而且执行采用 AOT 代码形式的方法速度更快。AOT 代码是原生代码,执行速度通常比解释的代码快(但是不太可能像 JIT 生成的代码那么快)。
  可以使用命令行选项定义 AOT 代码在共享类缓存可以占用的最小和最大空间,见清单 2。如果没有指定可以存储的 AOT 代码最大量,默认设置是使用整个缓存。但是,这不会导致整个缓存被 AOT 代码填满,因为只能从缓存中已有的类生成 AOT 代码。 图 1 说明共享类和 AOT 代码如何占用缓存空间,以及缓存空间设置如何控制它们使用的可用空间额度。 
  
  稍后进一步讨论 AOT 代码。
  类压缩
  为了尽可能提高使用共享类缓存的效率,JVM 使用压缩技术增加能够存储的类的数量。类压缩是自动执行的,无法通过命令行选项修改。
  持久化缓存
  IBM JRE for Java 5 中的共享类缓存是使用共享内存段实现的,这使 JVM 能够共享同一个缓存,但是操作系统重新引导之后缓存就失效了。这意味着,在重新引导之后启动的第一个 JVM 必须重新构建缓存。在 Java 6 中,缓存的默认实现改为使用内存映射文件。这使缓存持久化,在操作系统重新启动之后仍然有效。
  AOT 详解
  AOT 编译器是 IBM JRE for Java 6 中新增的编译机制。在使用以前的 IBM JRE 版本时,可以以两种方式执行 Java 方法:解释和编译。解释方式是解释并执行组成此方法的 Java 字节码。编译方式是由一个称为即时 (Just-in-Time,JIT) 编译器的 JRE 组件把代码编译和优化为原生机器码,然后执行。JIT 编译是自动执行的。在实际运行方法时执行编译过程,采用的编译技术取决于执行期间对实际方法的分析。
  什么是 AOT 代码?
  AOT 代码是通过 AOT 编译生成的 Java 方法的原生代码版本。与 JIT 编译不同,AOT 编译并不根据对 Java 方法的动态分析执行优化。通常,AOT 编译的原生代码版本比解释的 Java 字节码执行得快,但是没有 JIT 编译的原生代码那么快。
  AOT 编译的主要目的是,通过提供 Java 方法的预编译版本加快应用程序的启动速度。与生成 JIT 编译的代码相比,从共享类缓存装载这些预编译的 AOT 方法能够更快地获得可执行的 Java 方法原生代码版本。通过快速装载 AOT 编译的代码,JVM 可以更快地获得原生代码版本,减少解释 Java 方法所需的时间。AOT 编译的方法仍然属于 JIT 编译处理的范围,所以在最初以 AOT 代码形式执行方法之后,JIT 仍然可以进一步优化它。
  AOT 是共享类的组成部分
  生成的 AOT 代码存储在共享类缓存的一个区域中。使用这个共享类缓存的其他 JVM 都可以执行这些 AOT 代码,这样就避免了编译的开销。
  这种实现不同于实时 JVM;在实时 JVM 中,AOT 代码由一个实用程序 (jxeinajar) 执行编译并存储在 JAR 文件中,更多信息参见 "实时 Java,第 1 部分: 使用 Java 语言编写实时系统"。
  由 JVM 执行的 AOT 代码并不是直接共享的,而是从共享类缓存复制出来的。因为每个 JVM 仍然拥有 AOT 代码的拷贝,所以这种实现方式对于内存使用量没有直接的好处。但是,由于能够重用 AOT 代码并避免重复编译,可以节省内存和 CPU。
  AOT 问题诊断
  可以通过三个命令行选项帮助了解应用程序对哪些方法执行了 AOT 编译,以及这些方法在共享类缓存中占用多少空间: -Xjit:verbose :使用这个命令报告 JIT 执行的所有 AOT 编译。 
  -Xshareclasses:verboseAOT :使用这个命令报告在共享类缓存中存储或读取的所有 AOT 代码。 
  java -Xshareclasses:printAllStats :使用这个命令报告共享类缓存统计数据,包括存储的 AOT 代码和占用的空间。
  清单 3 显示的是在清空共享类缓存之后第一次 调用 Tomcat 服务器的输出,这里应用了运行时选项 -Xjit:verbose 和 -Xshareclasses:verboseAOT:  + (AOT cold) java/lang/StringBuilder.append(Ljava/lang/String;) Ljava/lang/StringBuilder; Storing AOT code for ROMMethod 0x02359850 in shared cache... Succeeded. + (AOT cold) sun/misc/URLClassPath$JarLoader.ensureOpen()V @ 0x0147BF9C-0x0147C106 Q_SZ=3 Storing AOT code for ROMMethod 0x023CBFC4 in shared cache... Succeeded. + (AOT cold) java/util/jar/JarFile.getEntry(Ljava/lang/String;) Ljava/util/zip/ZipEntry; Storing AOT code for ROMMethod 0x023CE38C in shared cache... Succeeded. 
  在启动 Tomcat 服务器之后,使用 java -Xshareclasses:printAllStats 命令获得共享类缓存统计数据,这显示存储在共享类缓存中的方法(清单 4 是部分输出): 如清单 5 所示,在以后使用共享类缓存调用 Tomcat 服务器时,会发现这些方法已经经过 AOT 编译,从缓存中装载它们即可,不需要重复编译:  Finding AOT code for ROMMethod 0x02359850 in shared cache... Succeeded. (AOT load) java/lang/StringBuilder.append(Ljava/lang/String;) Ljava/lang/StringBuilder; Finding AOT code for ROMMethod 0x023CBFC4 in shared cache... Succeeded. (AOT load) sun/misc/URLClassPath$JarLoader.ensureOpen()V Finding AOT code for ROMMethod 0x023CE38C in shared cache... Succeeded. (AOT load) java/util/jar/JarFile.getEntry(Ljava/lang/String;) Ljava/util/zip/ZipEntry; 
  AOT 编译通过启发式决策选择候选方法,这会进一步改进启动速度。因此,对应用程序的后续调用可能会导致更多的方法被 AOT 编译。
  对于已经 AOT 编译的方法,如果它满足必要的重新编译条件,就可能再执行 JIT 编译。但是,AOT 编译的目标是选择在应用程序启动时需要的方法,而 JIT 编译的目标是对频繁使用的方法进行优化,因此 AOT 编译的方法可能使用得不够频繁,不足以触发 JIT 编译。 清单 6 是使用 -Xjit:verbose 执行 SPECjbb2005 基准测试时的部分输出,其中包含两个方法的 AOT 编译报告:com/ibm/security/util/ObjectIdentifier.equals 和 java/math/BigDecimal.multiply。第一个方法并不进一步执行 JIT 编译,但是使用得比较频繁的 java/math/BigDecimal.multiply 会 JIT 编译两次,最终到达 hot 优化级别。 SPECjbb2005 的启动阶段并不长,所以只有几个方法执行 AOT 编译。注意,AOT 编译以 cold 优化级别执行,这反映 AOT 的总体目标是加快应用程序的启动。  + (AOT cold) com/ibm/security/util/ObjectIdentifier.equals(Ljav a/lang/Object;) Storing AOT code for ROMMethod 0x118B8AF4 in shared cache... Succeeded. + (AOT cold) java/math/BigDecimal.multiply(Ljava/math/BigDecima l;)Ljava/math/BigDecimal; Storing AOT code for ROMMethod 0x119D3C60 in shared cache... Succeeded. + (warm) java/math/BigDecimal.multiply(Ljava/math/BigDecima l;)Ljava/math/BigDecimal; + (hot) java/math/BigDecimal.multiply(Ljava/math/BigDecima l;)Ljava/math/BigDecimal; 
  java -Xshareclasses:printAllStats 命令产生的共享类缓存统计数据列出每个 AOT 编译的方法和缓存的每个共享类。可以通过这些信息了解共享类缓存的大小是否是合适的。例如,清单 7 说明缓存的总大小是 16776844 字节,只占用了其中的 40%,1668 个 ROMClass 占用 5950936 字节,458 个 AOT 编译的方法占用 683772 字节: 清单 8 是 -Xshareclasses:destroyAll 命令产生的输出,这表明缓存已经被销毁。这个命令还会发出消息 Could not create the Java virtual machine 。所以不必惊慌,这是正常的。 度量内存使用量
  可以使用许多性能工具检查共享类给内存使用量带来的好处。使用的工具取决于底层操作系统。在检查内存使用量时必须记住一点:缓存是通过一个内存映射文件实现的,这使多个虚拟机可以共享它的内容。用来检查内存使用量的工具必须能够区分共享内存(可由多个 JVM 访问和共享)和私有内存(只能由一个 JVM 访问)。
  Virtual Address Dump 实用程序 (Windows) Virtual Address Dump (vadump) 实用程序是 Microsoft?? 资源集中的一个工具,可以使用它提供关于应用程序或共享类缓存的内存使用量的信息。vadump 会产生大量信息,但是我们只需要关于工作集大小的报告,这会提供应用程序的内存使用量信息。vadump -os -p  命令显示给定的进程 ID 的工作集信息。 产生的输出包含关于一个进程使用的内存的大量信息。为了了解使用共享类所产生的内存改进,我们主要关注 Grand Total Working Set 部分,以及类数据共享如何影响 Private、Shareable 和 Shared 在这个数字中的比例。清单 9 显示一个 vadump 汇总输出示例。共享类是通过内存映射文件实现的,所以它们占用的内存显示在 Mapped Data 输出行中。  vadump -os -p 5364 Category Total Private Shareable Shared Pages KBytes KBytes KBytes KBytes Page Table Pages 29 116 116 0 0 Other System 8 32 32 0 0 Code/StaticData 2079 8316 5328 140 2848 Heap 87 348 348 0 0 Stack 4 16 16 0 0 Teb 1 4 4 0 0 Mapped Data 95 380 0 24 356 Other Data 61 244 240 4 0 Total Modules 2079 8316 5328 140 2848 Total Dynamic Data 248 992 608 28 356 Total System 37 148 148 0 0 Grand Total Working Set 2364 9456 6084 168 3204 
  要想找到 vadump 命令中需要的进程 ID,可以使用 Windows Task Manager: 打开 Task Manager 应用程序并选择 Processes 选项卡。
  找到称为 PID 的列。(如果没有出现这个列,那么单击 View > Select Columns 并选择 PID 复选框,见图 2)。
  找到希望检查的进程,记下 PID 列中的值。这就是需要传递给 vadump 的进程 ID。
  
  使用 top 度量 Linux 上的内存使用量 有许多 Linux 工具可以检查内存使用量。top 命令适合展示共享类的效果。为了让输出更容易理解,我们将在命令行上提供进程 ID 并以批模式运行此命令。清单 10 给出命令行和输出示例。  top -b -n 1 -p  top - 13:33:41 up 18 days, 9:30, 1 user, load average: 0.00, 0.00, 0.00 Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie Cpu(s): 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si Mem: 8157972k total, 311312k used, 7846660k free, 56448k buffers Swap: 2104472k total, 0k used, 2104472k free, 141956k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 7073 root 15 0 41616 13m 2228 S 0.0 0.2 5:43.70 X 
  下面的值是我们最感兴趣的: VIRT - Virtual Image (KB):此任务使用的虚拟内存总量。它包含所有代码、数据和共享库,以及已经换出的页面。
  RES - Resident size (KB):此任务使用的未交换物理内存。
  SHR - Shared Mem size (KB):此任务使用的共享内存量。它仅仅反映可以由其他进程共享的内存。
  通过配置 Eclipse 使用共享类特性
  为了展示使用共享类可以实现的内存使用量和启动改进效果,我们要度量它对两个应用程序的影响:Eclipse(代表客户端桌面应用程序)和 Apache Tomcat(代表服务器端应用程序)。
  正如在本文开头提到的,目前还没有针对 Windows 的单独的 IBM SDK for Java 6 和 Java 平台运行库。如果您使用 Windows(而不是 Linux 或 AIX),就需要下载 预构建的 Eclipse 包。
  如果使用 Linux 或 AIX,那么下载单独的 IBM SDK for Java 6 实现,然后从 Eclipse 项目网站下载所需的 Eclipse 版本(参见 参考资料)。按照 Eclipse 安装说明配置 Eclipse,让它能够使用 IBM SDK for Java 6。
  在安装 Eclipse 之后,还需要执行以下步骤: 为插件启用类共享。把 OSGI 插件适配器(参见 参考资料)安装到 Eclipse 插件目录中。
  下载 SampleView.jar(参见 下载)并把它安装到 Eclipse 插件目录中。在视图初始化时,这个插件连接 IBM JVM 跟踪并输出一些跟踪点,从而简化了对 Eclipse 启动时间的计时。我们将在下一节中讨论如何使用 IBM JVM 跟踪提供启动统计数据。
  创建两个工作空间 workspace1 和 workspace2。这样就可以启动两个 Eclipse 实例,让它们指向不同的工作空间,但是共享同一个类缓存。
  还需要设置 Tomcat(如果还没有设置的话)。只需从 Apache Tomcat 网站 下载这个应用程序,对下载包进行解压,然后按照文件 running.txt 中的说明操作。
  性能比较
  我们将使用前几节介绍的工具和应用程序度量共享类提供的性能收益。我们尽可能隔离共享类特性(通过尽可能禁用其他特性),以便更容易解释结果。
  Eclipse 性能:内存使用量
  为了检查内存使用量,我们使用不同的工作空间在同一个 Windows 上同时运行多个 Eclipse 实例。然后收集 vadump 数据对以三种不同模式启动的 Eclipse 进行比较: 以一般方式启动 Eclipse,不启用任何共享类功能。
  第一次用一个清空的共享类缓存启动 Eclipse。
  使用相同的共享类缓存启动第二个 Eclipse 实例。
  为了在 Eclipse 中启用共享类,需要创建一个新的启动命令行,其中应该包含正确的 JVM 选项。只创建一个新的快捷方式是不够的,而是应该创建一个用来启动 Eclipse 的批文件,见清单 11。它执行以下功能: 接受一个值为 1 或 2 的命令行参数,这两个值分别对应于在配置 Eclipse 时创建的工作空间。
  如果指定工作空间 1,那么清空已经存在的任何共享类缓存。
  在 Eclipse 终止运行之后,输出缓存统计数据。
  @echo off rem batch file to start Eclipse using the specified workspace SET ECLIPSE_HOME=C:\java\eclipse\IBMEclipse\eclipse SET JVM=C:\java\eclipse\IBMEclipse\ibm_sdk50\jre\bin\j ava.exe SET WNAME=C:\java\eclipse\workspace%1 SET SC_OPTS=-Xshareclasses:name=eclipse,verbose SET VMARGS=%SC_OPTS% echo Clearing shared classes cache if %1==1 %JVM% -Xshareclasses:destroyAll echo JVM version %JVM% -version echo Starting Eclipse %ECLIPSE_HOME%\eclipse.exe -nosplash -data %WNAME% -vm %JVM% -vmargs %VMARGS% %JVM% -Xshareclasses:name=eclipse,printStats 
  清单 12 给出不使用共享类的 Eclipse 实例的 vadump 报告。在 vadump 报告中,我们最感兴趣的字段是 Shareable KBytes、Shared KBytes 和 Grand Total Working Set KBytes。  Category Total Private Shareable Shared Pages KBytes KBytes KBytes KBytes Page Table Pages 54 216 216 0 0 Other System 28 112 112 0 0 Code/StaticData 4199 16796 11500 1052 4244 Heap 9400 37600 37600 0 0 Stack 98 392 392 0 0 Teb 21 84 84 0 0 Mapped Data 130 520 0 36 484 Other Data 5337 21348 21344 4 0 Total Modules 4199 16796 11500 1052 4244 Total Dynamic Data 14986 59944 59420 40 484 Total System 82 328 328 0 0 Grand Total Working Set 19267 77068 71248 1092 4728 
  清单 13 给出在使用 清单 11 中的批文件启动 Eclipse 时 vadump 的输出。可以看到有大约 4MB 的类(Shareable Mapped Data 为 4116 KBytes)被放在缓存中,这导致总工作集大小增加了相应的数量。突出显示的数据项说明内存可供其他进程共享。在比较 vadump 的输出时要记住一点:尽管这些输出是在 Eclipse 启动时产生的,但是报告的数字仍然有一些小差异。  Category Total Private Shareable Shared Pages KBytes KBytes KBytes KBytes Page Table Pages 54 216 216 0 0 Other System 28 112 112 0 0 Code/StaticData 4256 17024 11676 1072 4276 Heap 8631 34524 34524 0 0 Stack 103 412 412 0 0 Teb 20 80 80 0 0 Mapped Data 1155 4620 0 4116 504 Other Data 5386 21544 21540 4 0 Total Modules 4256 17024 11676 1072 4276 Total Dynamic Data 15295 61180 56556 4120 504 Total System 82 328 328 0 0 Grand Total Working Set 19633 78532 68560 5192 4780 Current statistics for cache "eclipse": base address = 0x42B0E000 end address = 0x43B00000 allocation pointer = 0x42E0B958 cache size = 16776844 free bytes = 12005976 ROMClass bytes = 4001256 AOT bytes = 625428 Data bytes = 57043 Metadata bytes = 87141 Metadata % used = 1% # ROMClasses = 1334 # AOT Methods = 480 # Classpaths = 4 # URLs = 0 # Tokens = 0 # Stale classes = 0 % Stale classes = 0% 
  启动另一个 Eclipse 实例,然后在此实例上运行 vadump,输出见清单 14。初看上去,内存使用量的差异非常小。但是仔细观察就会发现,4MB 的内存(Shared Mapped Data 为 4564 KBytes)实际上是与另一个进程共享的。对于使用共享内存的每个进程,vadump(和 Task Manager)把共享内存都计算在 Grand Total Working Set 之内。第二个 Eclipse 实例的内存使用量低 4MB,这是因为它共享由第一个 Eclipse 实例创建并填充的类缓存。
  这里给出的结果反映 Eclipse 中只安装了很少几个插件时的启动情况。如果安装更多的插件,就会有更多的类被放在共享类缓存中,启动时间也会有相应的改进。  Category Total Private Shareable Shared Pages KBytes KBytes KBytes KBytes Page Table Pages 54 216 216 0 0 Other System 29 116 116 0 0 Code/StaticData 4254 17016 11676 0 5340 Heap 8684 34736 34736 0 0 Stack 98 392 392 0 0 Teb 20 80 80 0 0 Mapped Data 1150 4600 0 36 4564 Other Data 5261 21044 21040 4 0 Total Modules 4254 17016 11676 0 5340 Total Dynamic Data 15213 60852 56248 40 4564 Total System 83 332 332 0 0 Grand Total Working Set 19550 78200 68256 40 9904 
  Eclipse 性能:启动
  除了对内存使用量的改进之外,由于从缓存(而不是磁盘)装载类,共享类还会减少启动时间。另外,使用缓存中的 AOT 代码也有助于减少启动时间。为了统计 Eclipse 的启动时间,我们将使用一个定制的视图(参见 本文前面的说明),它在装载时使用 IBM JVM 跟踪输出消息。还必须修改 清单 11 所示的 Eclipse 启动批文件,以便启用 JVM 跟踪并记录以下跟踪事件: 跟踪的初始化:跟踪在 JVM 启动之后几乎立即启动,这发生在装载任何类之前。我们以此作为启动时间计时的起点。 
  示例视图消息:当视图初始化时输出第一个消息,收到这个消息就说明 Eclipse 已经启动了。我们以此作为启动时间计时的终点。
  清单 15 给出修改后的批文件,增加的 JVM 跟踪配置行以粗体显示:  @echo off rem batch file to time Eclipse startup SET ECLIPSE_HOME=C:\java\eclipse\IBMEclipse\eclipse SET WNAME=C:\java\eclipse\workspace%1 SET JVM=C:\java\eclipse\IBMEclipse\ibm_sdk60\jre\bin\j ava.exe SET TRACE_OPTS=-Xtrace:iprint=tpnid{j9trc.0},iprint=Sa mpleView SET SC_OPTS=-Xshareclasses:name=eclipse,verbose SET VMARGS=%SC_OPTS% %TRACE_OPTS% echo Clearing shared classes cache if %1==1 %JVM% -Xshareclasses:destroyAll echo JVM version %JVM% -version echo VM arguments echo %VMARGS% echo Starting Eclipse %ECLIPSE_HOME%\eclipse.exe -nosplash -data %WNAME% -vm %JVM% -vmargs %VMARGS% %JVM% -Xshareclasses:name=eclipse,printStats 
  清单 16 和清单 17 分别给出在不启用和启用共享类的情况下启动 Eclipse 时的输出。可以看到启动时间有大约 1 秒的改进,这表示启动时间减少了 25%。启用共享类时的计时结果反映的是第二个 Eclipse 的启动情况,因为第一个 Eclipse 实例用来填充缓存。对于这个非常 "干净" 的 Eclipse 版本,在缓存中只存储 4MB 的数据;对于更大更复杂的 Eclipse 应用程序,会更充分地利用类共享减少启动时间。  09:30:40.171*0x41471300 j9trc.0 - Trace initialized for VM = 000962A8 [-Xshareclasses verbose output enabled] JVMSHRC158I Successfully created shared class cache "eclipse" JVMSHRC166I Attached to cache "eclipse", size=16777176 bytes 09:30:43.484 0x41471300SampleView.2 - Event id 1, text = Mark 09:30:43.484 0x41471300SampleView.0 > Entering getElements(Object parent) 09:30:43.484 0x41471300SampleView.1 删除 "-Xshareclasses:name=tomcat,verbose"),然后在启用共享类的情况下再测试一次。然后,启动第二个 Tomcat 实例,以此展示共享同一个类缓存的两个 Tomcat 进程的内存使用量差异。清单 19、清单 20 和清单 21 分别给出这三种情况下的 top 输出。清单 22 给出共享类缓存统计数据。  Tasks: 2 total, 0 running, 2 sleeping, 0 stopped, 0 zombie Cpu(s): 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si Mem: 8157972k total, 1766440k used, 6391532k free, 101152k buffers Swap: 2104472k total, 0k used, 2104472k free, 1376084k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 24621 jbench 17 0 78440 56m 14m S 0.0 0.7 0:04.08 java 24674 jbench 16 0 77600 51m 14m S 0.0 0.6 0:02.28 java 
  在刚看到启用和不启用共享类的 Tomcat 内存使用量对比结果时,看不出启用共享类的好处,因为内存使用量数字增加了。但是,如果仔细研究这些数字,就会看出真实的情况: SHR 增加了大约 6MB(从 8400KB 增加到 14MB)。这是存储在共享类缓存中的数据量。
  RES 略微增加了(从 54MB 增加到 56MB),这是由支持共享类所需的基础结构(对象库等)造成的。
  VIRT 增加了,因为它是 SHR 和 RES 增加的值之和。
  在启动第二个 Tomcat 实例并使用 top 检查内存使用量时,可以看到第二个实例(清单 21 中的进程 24674)的共享内存量是相同的(都是 14MB SHR),但是 RES 减少了 5MB(从 56MB 减少到 51MB),虚拟内存也减少了。与 Windows 上的 vadump 一样,top 会正确地识别出可能被共享的内存,但是并不显示实际连接到共享内存的其他进程。在这个示例中,两个 Tomcat 实例使用同一个共享类缓存,所以它们的总内存使用量会减少。在这个测试中,Tomcat 服务器只使用了缓存中不到一半的可用空间。清单 22 显示放在缓存中的可共享 ROMClass 数据有 5911028 字节(略微少于 6MB),这说明通过共享缓存中的类有可能进一步减少内存使用量。 然后,将这一时间与启用共享类时 Tomcat 的启动时间(见清单 24)进行比较。 清单 24. Tomcat 启动时间,启用共享类,使用 AOT 代码
  可以看到,共享类使 Tomcat 启动时间从 1138ms 减少到了 851ms,这表示启动时间减少了 25%。这一改进是由启用类共享和使用 AOT 代码共同造成的。为了看到 AOT 代码产生多大的好处,可以使用命令行选项 -Xnoaot 禁止使用 AOT 代码(见清单 25),然后再测试启动时间: 可以看到清单 25 显示的时间增加了,这说明在共享类缓存中存储 AOT 代码对减少 Tomcat 启动时间有很大好处。
  结束语
  本文展示了共享类在改进 Java 应用程序的启动时间和减少内存使用量两方面的效果。我们以 Tomcat 和 Eclipse 为例,演示了如何量化共享类特性对内存使用量和启动提供的好处。当然,应用程序的运行方式各不相同,因此获得的收益也不相同。但是,即使对于像这里提供的示例这样简单的配置,也会显著减少启动时间。
  请记住,当多个应用程序运行同一级别的 IBM SDK 时,会获得最大的收益,因为它们有最多的东西可以共享。但是,即使是单一应用程序,也可以通过使用共享类缓存改进启动时间。
  另外,我们讲解了如何通过工具(比如 Windows 上的 vadump 和 Linux 上的 top)重复计算共享内存,从而更准确地度量类共享所节省的内存量。尽管这些工具提供的内存使用量视图并不完美,但是我们讲解了如何读懂数据的含义。
分享到:
评论

相关推荐

    c++共享内存,提高性能。

    c++共享内存,提高性能。

    Oracle_Database10g_性能调整与优化-第10章_使用PLSQL提高性能

    Oracle 10g使PL/SQL又进了一步。本章将主要介绍10g中有用的...如何使用PL/SQL更好地调整SQL很有可能就是影响性能的最大驱动因素,当然,本章节中也将介绍其他的调整方案。本章的第一部分着重介绍对PL/SQL的理解和定位。

    Gazelle是一款高性能用户态协议栈 它基于DPDK在用户态直接读写网卡报文,共享大页内存传递报文,使用轻量级LwIP协议栈

    它基于DPDK在用户态直接读写网卡报文,共享大页内存传递报文,使用轻量级LwIP协议栈。能够大幅提高应用的网络I/O吞吐能力。专注于数据库网络性能加速,如MySQL、redis等。兼顾高性能与通用性:高性能。报文零拷贝,...

    PC软件最全系列一键修复共享打印机 免费下载

    除了修复问题,该软件还提供了一些优化功能,可以帮助提升共享打印机的性能和稳定性。例如,它可以清理无效的打印机队列、更新过期的驱动程序、优化打印机设置等,从而改善打印机的工作效率。 总而言之,PC软件的...

    在脉冲C中提高共享内存通信的性能

    随着现场可编程门阵列(FPGA)到Million-Gate范围的发展,高级语言在电子系统设计中日益流行,这大大提高了设计和... 实验结果表明,改进后的实现可以大大提高共享内存通信的性能,并进一步提高硬件过程的执行效率。

    大势至共享文件审计系统

    大势至共享文件审计系统分为软件版本和硬件版本:其中,硬件版本基于全面优化的高性能文件服务器专用平台,比同类配置的普通服务器性能提升至少30%(详情见下文硬件参数)。 平台架构 2U工业设计,高强度钢外壳 ...

    高性能网站建设进阶

    在本书中Souders与8位专家分享了提升网站性能的实践和实用建议,主要包括:理解Ajax性能,编写高效的,创建快速响应的应用程序和无阻塞加载脚本,跨域共享资源,无损压缩图片大小,使用块编码加快网页渲染,避免或...

    无线电频谱共享

    无线电频谱随着无线业务的发展,越来越多的用户接入到无线系统中,从而...一定区域的用户利用各自的天线形成一个协同簇,彼此之间可以通过共享天线作为中继,形成一个虚拟的MIMO系统,从而获得分集增益,改善通信性能。

    R语言中首尔共享单车需求数据集的回归分析.zip (有word报告*期末大作业)

    使用R2、均方根误差、平均绝对误差和变异系数等多重评估指标来衡量回归模型的预测性能。结果表明,基于规则的CUBIST模型能够解释汉城自行车测试集中约95%的方差(R2)。对所有开发的模型进行了变量重要性分析,以...

    负载均衡3中session共享demo

    大量的并发访问或数据流量分担到多台...从而提高性能,和吞吐量;但是负载均衡有一个问题就是不同的服务器间进行session共享问题的处理;肯定不可能每分配到一个服务上让用户进行一次登录,这样的用户体验会是极差的;

    Java性能优化

    使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面: 控制资源的使用,通过线程同步来控制资源的并发访问; 控制实例的产生,以...

    一款高性能用户态协议栈 它基于DPDK在用户态直接读写网卡报文,共享大页内存传递报文,使用轻量级LwIP协议栈

    它基于DPDK在用户态直接读写网卡报文,共享大页内存传递报文,使用轻量级LwIP协议栈。能够大幅提高应用的网络I/O吞吐能力。专注于数据库网络性能加速,如MySQL、redis等。兼顾高性能与通用性:报文零拷贝,无锁,...

    Femtocell增强的蜂窝移动通信系统中频谱资源共享技术研究

    本文研究Femtocell和MacroceU之间如何合理地共享频谱资源,以使两个嘲络都能 达到较高的性能。 本文分析在单个和多个接入子信道的情况下,系统的上/下行平均容量、FemtoceU平 均容量、Macrocell平均容量与Femtocell...

    论文研究-MIMO认知无线电系统中协作频谱共享方案.pdf

    针对一对主用户收发链路和一对次用户收发链路构成的多输入多输出(MIMO)认知无线电系统,为有效提高主用户和次...仿真结果表明,与传统机会频谱共享方案相比,协作频谱共享方案可以显著提高主用户和次用户的系统性能。

    UNIX-IBMAIX5L参考-性能管理指南.chm

    使用性能工具箱进行连续的系统性能监视 初始性能诊断 报告的性能问题的类型 性能限制资源的确定 工作负载管理诊断 资源管理概述 处理器调度程序性能概述 虚拟内存管理器(VMM)性能概述 固定磁盘存储管理的性能概述 ...

    提高Oracle数据库性能的四个误区

    为了提高性能,我们针对Oracle数据库本身提供了的方法或方案进行过不少的尝试,主要包括: 共享服务器模式(MTS);集群技术(Clustering)RAC;分区; 并行处理(主要是并行查询)。 Oracle提供的这些特性确实是用来进行...

    微服务框架下数据共享技术研究和实现

    加,数据规模的不断增大,单体架构在数据共享中的可扩展性和性能稳定性明显不 足,同时,现有数据共享平台的数据滥用和隐私泄露问题,导致数据共享缺乏必要 的安全保障。 针对上述问题,本文选用 Spring Cloud 技术...

    论文研究-一个基于第三方数据传送的SAN数据共享系统.pdf

    在SAN 环境下实现了一个开放系统与大型机数据共享系统, 克服了传统客户/ 服务器数据共享模型的缺点, 能有效地提高系统的数据传送性能; 提出了一种基于共享磁盘映射关系的第三方数据传送方法, 由于共享磁盘映射关系是...

    Springboot学生读书笔记共享.zip

    使用Redis作为缓存,提高系统性能。采用分页插件实现分页功能,优化数据加载速度。总之,SpringBoot学生读书笔记共享平台为用户提供了一个便捷、高效的读书笔记共享环境,有助于学生之间的知识交流和学习进步。

    雾计算中基于属性的安全数据共享和有效撤销

    该体系结构试图超越基于云的体系结构,并确保进一步提高系统性能,尤其是从安全性角度而言。 我们将解决数据共享的安全挑战,例如细粒度的访问控制,数据机密性,抗串通性,可伸缩性以及用户吊销问题。 牢记这些...

Global site tag (gtag.js) - Google Analytics