【问题标题】:Printing class histogram programmatically以编程方式打印类直方图
【发布时间】:2011-04-15 04:42:50
【问题描述】:

有没有办法以编程方式打印当前 Java 应用程序中使用最多的 N 个类?

样本输出:N=10

num   #instances    #bytes  class name
--------------------------------------
  1:        23     4723136  [I
  2:        19     4718928  [J
  3:        18     4718880  [D
  4:     73925     1774200  java.lang.String
  5:       208     1226400  [C
  6:        28     1205064  [B
  7:        18     1179936  [F
  8:        68      297040  [Ljava.lang.String;
  9:       332       14136  [Ljava.lang.Object;
 10:        32       10240  <objArrayKlassKlass>

【问题讨论】:

  • Netbeans 分析器做得很好,Netbeans 是用 Java 编写的,所以...是的,有一种方法 =) 给你!
  • “最常用”是什么意思?在给定时间点或整个应用程序生命周期内实例数量最多的类?或者你的意思是那些方法被调用最多的类? (基于时间的测量或调用计数测量)
  • 什么是[J? [I 是整数数组,但是 J?

标签: java class instance histogram


【解决方案1】:

可能不会不经过JVM Tool Interface (JVM TI) 或篡改 Object 的实现(这是一项棘手的工作)。

这篇文章也许有用:Creating a Debugging and Profiling Agent with JVMTI。

【讨论】:

【解决方案2】:

如果您的意思是最常用的类,初始化最多的类,您可以在构造函数周围定义一个切入点并跟踪每种类型的初始化。您可以使用 AspectJ。

【讨论】:

  • 我想用 jmap -histo[:live] pid 做同样的事情而不改变我们的程序。因为系统是活的。我不想通过添加这样的切分来增加开销。我也不能相信终结器方法来减少计数。
  • 所以你想查看给定点的实例数,我以为你想查看创建的实例总数。然后我认为您需要的是分析 API。您可以检查 java.lang.management 包,但我不确定是否可以获得实例计数
【解决方案3】:

我认为您不能在同一个 JVM 中执行此操作,因为您需要遍历对象堆,并且您最终可能会陷入无限循环。只是出于好奇,我尝试使用 Runtime.exec 针对同一个 JVM 甚至针对不同的 JVM 生成 jmap,但它只是挂起?

String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
//pid=2520
System.out.println("PID: " + pid);
Process p = Runtime.getRuntime().exec("jmap -histo " + pid);
p.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line ;
while((line = br.readLine())!=null){
    System.out.println(line);
}

br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while((line = br.readLine())!=null){
    System.out.println(line);
}

【讨论】:

【解决方案4】:

您可以启动 jmap 作为您的 java 包装脚本的一部分并在循环中连续运行它:

例如,如果你在 Unix 上,你可以这样做:

java MyMainClass ... &

pid=$!
while [ ! -z $pid ]
do
    jmap -histo $pid | head -13
    sleep 60

    #check pid
    kill -0 $pid > /dev/null 2>&1   
    if [ $? -ne 0 ]
    then
       pid=""
    fi  
done

【讨论】:

    【解决方案5】:

    您可以像这样以编程方式使用Runtime#exec 来运行jmap:

    String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
    Process p = Runtime.getRuntime().exec("jmap -histo " + pid);
    
    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
    br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
    while ((line = br.readLine()) != null) {
        System.out.println("ERR:" + line);
    }
    p.waitFor();
    

    输出将是:

     num     #instances         #bytes  class name (module)
    -------------------------------------------------------
       1:         55905       40469648  [B (java.base@11.0.2)
       2:          5292        7240256  [I (java.base@11.0.2)
       3:         24865         596760  java.lang.String (java.base@11.0.2)
       4:          5422         347008  java.net.URL (java.base@11.0.2)
       5:          5242         327088  [Ljava.lang.Object; (java.base@11.0.2)
    ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-27
      相关资源
      最近更新 更多