为什么我的Oracle JVM为简单的“Hello World”创建了所有这些对象。程序?

时间:2017-06-02 16:12:51

标签: java jvm

我正在玩jmap,发现那个简单的" Hello World" Java程序创建了数千个对象。以下是 Oracle JVM更新131 在启动时创建的截断对象列表:

 num     #instances         #bytes  class name
----------------------------------------------
   1:           402        4903520  [I
   2:          1621         158344  [C
   3:           455          52056  java.lang.Class
   4:           194          49728  [B
   5:          1263          30312  java.lang.String
   6:           515          26088  [Ljava.lang.Object;
   7:           115           8280  java.lang.reflect.Field
   8:           258           4128  java.lang.Integer
   9:            94           3760  java.lang.ref.SoftReference
  10:           116           3712  java.util.Hashtable$Entry
  11:           126           3024  java.lang.StringBuilder
  12:             8           3008  java.lang.Thread
  13:            74           2576  [Ljava.lang.String;
  14:            61           1952  java.io.File
  15:            38           1824  sun.util.locale.LocaleObjectCache$CacheEntry
  16:            12           1760  [Ljava.util.Hashtable$Entry;
  17:            53           1696  java.util.concurrent.ConcurrentHashMap$Node
  18:            23           1472  java.net.URL
  19:            14           1120  [S
  20:             2           1064  [Ljava.lang.invoke.MethodHandle;
  21:             1           1040  [Ljava.lang.Integer;
  22:            26           1040  java.io.ObjectStreamField
  23:            12           1024  [Ljava.util.HashMap$Node;
  24:            30            960  java.util.HashMap$Node
  25:            20            800  sun.util.locale.BaseLocale$Key

我知道JVM从JAR文件加载类,并期望看到java.lang.Classjava.lang.String[Ljava.lang.Object。我也清楚了258 java.lang.Integer个对象:这是Integer缓存。

但是java.lang.reflect.FieldHashtable?很多StringBuilder s? java.util.concurrent.ConcurrentHashMap?这是从哪里来的?

程序非常简单:

public class Test {
    public static void main(String[] args) throws IOException {
        System.out.println("Hello world");
        System.in.read();
    }
}

JVM详细信息:

java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

Ubuntu 16.04。

3 个答案:

答案 0 :(得分:21)

您可以通过使用-XX:+TraceBytecodes标志运行应用程序来自行找到答案 此标志位于debug builds of HotSpot JVM

下面是详细的Flame Graph(可点击的SVG),显示了分配对象来自的堆栈跟踪。

JDK start-up allocation

就我而言,启动分配的主要来源是

  • URLClassLoader和Extension ClassLoader
  • 区域设置缓存
  • UsageTrackerClient
  • MetaIndex registry
  • 系统属性
  • Charset初始化

P.S。 The script用于从TraceBytecodes输出生成Flame Graph

答案 1 :(得分:3)

有很多维护数据结构。例如。每个初始化的JVM都有system properties,这是Hashtable的子类型,因此解释了Hashtable.Entry个实例。

此外,像java.lang.Character这样的核心类知道所有字符的Unicode属性,你也看到Locale - 你的统计数据中的特定类,因为这些类必须在启动时正确初始化。这些示例如此有趣的原因是,它们是从文件或嵌入资源加载这些信息,因此它们的初始化涉及I / O和缓存机制,这些机制在输出中看到了它们。

此外,在启动过程中创建的其他对象可能尚未进行垃圾回收。有很多操作,比如处理类路径和由它指定的jar文件或解析命令行选项,这些操作比最后将执行的“Hello World”程序更复杂。请注意,您可以创建堆转储而不仅仅是直方图,这样您就可以看到谁拥有对现有对象的引用。

答案 2 :(得分:2)

检查工具是否加载其他类

我尝试了以下程序:

package test;
public class MainSleep {
    public static void main(String[] args) throws InterruptedException {
        synchronized (MainSleep.class) {
            MainSleep.class.wait(5*1000);
        }
    }
}

当我用它运行时:

"c:\Program Files\Java\jdk1.8.0_131\bin\java" \
  -verbose:class -cp target\classes test.MainSleep

我得到详细的类加载消息,然后暂停5秒,然后关闭会加载更多类:

...
[Loaded sun.misc.PerfCounter from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded sun.misc.Perf$GetPerfAction from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded sun.misc.Perf from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded sun.misc.PerfCounter$CoreCounters from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded sun.nio.ch.DirectBuffer from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.nio.MappedByteBuffer from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.nio.DirectByteBuffer from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.nio.LongBuffer from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.nio.DirectLongBufferU from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.security.PermissionCollection from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.security.Permissions from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.net.URLConnection from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded sun.net.www.URLConnection from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded sun.net.www.protocol.file.FileURLConnection from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded sun.net.www.MessageHeader from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.io.FilePermission from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.io.FilePermission$1 from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.io.FilePermissionCollection from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.security.AllPermission from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.security.UnresolvedPermission from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.security.BasicPermissionCollection from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded test.MainSleep from file:/D:/ws/BIS65/test-java8/target/classes/]
[Loaded sun.launcher.LauncherHelper$FXHelper from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.lang.Class$MethodArray from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.lang.Void from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
...
[Loaded java.lang.Shutdown from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from c:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]

所以这将是基线。当我现在在该文件上使用jstackjmap并检查详细的类加载消息时,我可以看到它是否引入了新类(当然不确定实例)。

使用jstack -ljstack,会加载以下额外的一个类:

[Loaded java.lang.Class$MethodArray from
[Loaded java.lang.Void from
[Loaded java.util.concurrent.locks.AbstractOwnableSynchronizer ...
[Loaded java.lang.Shutdown from
[Loaded java.lang.Shutdown$Lock from

使用jstack -Fjstack -m没有(!)加载其他类:

[Loaded java.lang.Class$MethodArray from
[Loaded java.lang.Void from
[Loaded java.lang.Shutdown from
[Loaded java.lang.Shutdown$Lock from

jmap -clstat-finalizerinfo-heap-histo-histo:live无法加载其他类:

[Loaded java.lang.Class$MethodArray from
[Loaded java.lang.Void from
[Loaded java.lang.Shutdown from
[Loaded java.lang.Shutdown$Lock from

对于带有和不带-F选项的jmap -dump:format=b,file=ignore.hprof以及带有和不带live标志的情况,情况也是如此。

为了完整起见,如果我使用 jvisualvm jconsole ,它将始终为线程,堆和应用程序快照触发大量JMX类加载。很可能是因为它总是打开流程的仪表板。

探索堆内容

现在我们已经建立了这个,我看了一下MAT的jmap -dump:format=b(非实时,非强制)堆转储,寻找你感兴趣的Fields:

MAT 无法访问的对象直方图(显示在堆中找到但未连接到任何GC根的实例,基本上所有尚未收集的垃圾)有3038个对象,前10个:

Class Name                               | Objects | Shallow Heap
------------------------------------------------------------------
char[]                                   |   1.026 |      113.848
java.lang.String                         |     599 |       14.376
int[]                                    |     423 |        7.664
java.lang.Object[]                       |     220 |       14.192
java.lang.StringBuilder                  |     137 |        3.288
java.lang.reflect.Field                  |     115 |        8.280
java.lang.ProcessEnvironment$CheckedEntry|      66 |        1.056
java.io.File                             |      59 |        1.888
java.lang.Class                          |      32 |            0
java.lang.StringBuffer                   |      30 |          720

目前没有单一的实时Field实例可见MAT,只有非常有限的Class个实例。这看起来很像.hprof或MAT问题:类实例似乎没有在堆转储中显示它们的任何字段。我认为它们应该由Class#reflectionData : SoftReference<ReflectionData<T>>引用(!)引用,但我认为这应该在堆转储中可见,而不会丢失115个字段。 (实时堆中没有Class$ReflectionData,无法到达的组织中有Class$ReflectionData。这可以很好地适应115 Fields。

(我想我会回来查看Serviceability-dev @ openjdk。这不适合评论,所以这是一个不完整的答案,但我打算加强它。)