39 很多代码无关的int[]来自于哪里

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了39 很多代码无关的int[]来自于哪里相关的知识,希望对你有一定的参考价值。

前言

最近看到了这样的一个问题,在 perfma 的论坛上面  

不起眼,但是足以让你有收获的JVM内存分析案例

原问题的帖子是在 

为什么内存中存在很多代码无关的int[]数组?

然后 我也吧这个测试用例复制下来了, 跑了一下, 看了看, 这里整理下一些 相关的收获 

以下的相关代码截图 基于 jdk8

测试代码

测试代码来自于, 问题原帖子, 这里做了一些小的调整 

package com.hx.test11;

/**
 * Test30BigIntArray
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2020/8/12 21:58
 */
public class Test30BigIntArray 

    // identStr
    private String identStr = "identStr";
    // mCache
    final static List<Method> mCache = new Vector<Method>();

    public static void main(String[] args) throws Throwable 
        final File f = new File(".\\\\target\\\\classes");
        int i = 0;
        while (true) 
            new Thread(new Runnable() 
                @Override
                public void run() 
                    try 
                        ClassLoader1 classLoader1 = new ClassLoader1(new URL[]f.toURI().toURL());
                        ClassLoader2 classLoader2 = new ClassLoader2(new URL[]f.toURI().toURL());
                        ClassLoader3 classLoader3 = new ClassLoader3(new URL[]f.toURI().toURL());
                        final Class<?> model1Clz = classLoader1.loadClass("com.hx.test11.Test30BigIntArray$TbmkModel1");
                        final Class<?> model2Clz = classLoader2.loadClass("com.hx.test11.Test30BigIntArray$TbmkModel2");
                        final Class<?> model3Clz = classLoader3.loadClass("com.hx.test11.Test30BigIntArray$TbmkModel3");
                        final TbmkModel1 model1 = new TbmkModel1();
                        final TbmkModel2 model2 = new TbmkModel2();
                        final TbmkModel3 model3 = new TbmkModel3();

                        for (int i = 0; i < 1000; ++i) 
                            int methodIdx = i % 10;
                            Method m = model1Clz.getMethod("method" + methodIdx);
                            m.invoke(model1);
                            mCache.add(m);
                            m = model2Clz.getMethod("method" + methodIdx);
                            m.invoke(model2);
                            mCache.add(m);
                            m = model3Clz.getMethod("method" + methodIdx);
                            m.invoke(model3);
                            mCache.add(m);
                        
                        System.out.println("mCache size: " + mCache.size());
                        Thread.sleep(100);
                     catch (Exception e) 
                        e.printStackTrace();
                    
                
            
            ).start();

            Thread.sleep(10);
            i++;
            System.out.println(" i = " + i);
            if (i == 3) 
                Thread.sleep(2000_000);
            
        
    

    /**
     * ClassLoader1
     *
     * @author Jerry.X.He <970655147@qq.com>
     * @version 1.0
     * @date 2020/8/12 22:00
     */
    static class ClassLoader1 extends URLClassLoader 
        public ClassLoader1(URL[] urls) 
            super(urls);
        
    

    static class ClassLoader2 extends URLClassLoader 
        public ClassLoader2(URL[] urls) 
            super(urls);
        
    

    static class ClassLoader3 extends URLClassLoader 
        public ClassLoader3(URL[] urls) 
            super(urls);
        
    

    /**
     * TbmkModel1
     *
     * @author Jerry.X.He <970655147@qq.com>
     * @version 1.0
     * @date 2020/8/12 22:02
     */
    static class TbmkModel1 
        public void method0() 

        

        public void method1() 

        

        public void method2() 

        

        public void method3() 

        

        public void method4() 

        

        public void method5() 

        

        public void method6() 

        

        public void method7() 

        

        public void method8() 

        

        public void method9() 

        
    

    static class TbmkModel2 
        public void method0() 

        

        public void method1() 

        

        public void method2() 

        

        public void method3() 

        

        public void method4() 

        

        public void method5() 

        

        public void method6() 

        

        public void method7() 

        

        public void method8() 

        

        public void method9() 

        
    

    static class TbmkModel3 
        public void method0() 

        

        public void method1() 

        

        public void method2() 

        

        public void method3() 

        

        public void method4() 

        

        public void method5() 

        

        public void method6() 

        

        public void method7() 

        

        public void method8() 

        

        public void method9() 

        
    



问题的调试

本文的调试时站在前人的肩膀上面进行的思考, 感谢 你假笨 大佬 

因此本文的主要出发点就是从 CollectedHeap::fill_with_array 开始 

当然 要是问题的原点来找的话, 应该是 vm层面从 Universe::intArrayKlassObj() 开始查找 

业务代码, 以及依赖的代码这边 应该是从 CollectedHeap::array_allocate 来找吧

呵呵 不过我们这里 暂时只关心问题中提到的这些大的 int[] 

为了更方便的验证这个问题, 我对 collectedHeap.fill_with_array 做了少量的调整, 主要是增加了一些日志输出, 是如下面的代码中的 tty->print_cr 的上下三行 

collectedHeap.cpp 

void
CollectedHeap::fill_with_array(HeapWord* start, size_t words, bool zap)

  assert(words >= filler_array_min_size(), "too small for an array");
  assert(words <= filler_array_max_size(), "too big for a single object");

  const size_t payload_size = words - filler_array_hdr_size();
  const size_t len = payload_size * HeapWordSize / sizeof(jint);
  assert((int)len >= 0, err_msg("size too large " SIZE_FORMAT " becomes %d", words, (int)len));

  // Set the length first for concurrent GC.
  ((arrayOop)start)->set_length((int)len);
  if(Thread::current()->is_Java_thread()) 
      tty->print_cr(" thread: %s, start %p, len : %d ", Thread::current()->name(), start, (int) len);
  
  post_allocation_setup_common(Universe::intArrayKlassObj(), start);
  DEBUG_ONLY(zap_filler_array(start, words, zap);)

vm 参数如下 

-Xmx128m

运行时结果如下 

使用 hsdb 查看当前进程的信息  

从上面可以看出, 这部分大的 int[] 的确是来自于 collectedHeap.fill_with_array 

并且有一部分小的得 int[] 也是来自于 collectedHeap.fill_with_array 

collectedHeap.fill_with_array 产生的小的 int[] 

在下图的地方打上断点, 断点 hit 

可以看出的是 当前是正在分配某一个类型的数组, 尝试从 tlab 中分配, 那么为什么又会 创建 int[] 呢? 

切换一下栈帧, 来到 CollectedHeap.allocate_from_tlab_slow 

可以看到 之所以 需要创建int[] 的原因是, 当前线程目前的 tlab 的空间已经不够了, 所以 需要吧当前线程的 tlab 的剩余的空间填充一个 dummy 的 int[], 最终会被 gc 掉 

然后 后面走的是 新建一块 tlab 的空间, 然后再 尝试业务上面的内存分配 

collectedHeap.fill_with_array 产生的大的 int[] 

接下来我们再来看一下 这部分的大的 int[] 

从栈帧信息中可以看出是在 线程退出的时候, 创建的 int[], 那么为什么 需要创建这个 int[] 呢? 

切换一下栈帧来到 JavaThread.exit 

在线程退出的时候, 需要将 当前 tlab 剩余的空间填充一个 int[], 注释这里也将这么做的结果阐述的很清楚 

jmap 的时候产生的 int[] 

上面 你假笨 提到了 jmap 的时候, 也会触发 collectedHeap.fill_with_array 的调用 

接下来我们试试, 如下命令, 触发 jmap 

jmap -dump:format=b,file=/root/Desktop/openJdk8/VM/logs/javapid_59049.hprof 59049

断点信息如下图 

我们吧栈帧切换到 VM_HeapDumper.doit 上面 

在执行业务之前, 需要把 各个线程的 tlab 剩余的部分使用 int[] 来填充, 便于这种遍历整个堆的业务的执行 

其他的填充 int[] 的场景 

1. 从 collectedHeap.fill_with_object 作为出发点来看 

2. 从 Universe::intArrayKlassObj 往外面找 

3. 从 CollectedHeap::array_allocate 往外面找 

大量的 int[0] 来自于哪里

从上面的问题, 我衍生发现了另外的一个问题, 从 hsdb 上面可以看到 大量的 int[0] 

那么这些对象来自于哪里呢? 

总共有 553 个 int[] 

统计了一下 : 差不多是有 502 个 int[0], 51 个有长度的 int[]

Universe::intArrayKlassObj 的初始化 

类加载的时候创建的 int[0]

类加载的时候也会创建一个 int[0], 作为锁 

为 java.lang.Object.class 创建的 int[0]

类的数量 : 499 个

当前 gc 的次数 : 0次

root@ubuntu:~# jstat -gcutil 59427
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00   0.00  32.01   0.00      �      0    0.000     0    0.000    0.000

其他地方创建的 int[0]

业务代码可以通过 new int[0] 来创建 

由于精力有限, 其他的情况就暂时不在深究下去了 

完 

参考

为什么内存中存在很多代码无关的int[]数组?

不起眼,但是足以让你有收获的JVM内存分析案例

以上是关于39 很多代码无关的int[]来自于哪里的主要内容,如果未能解决你的问题,请参考以下文章

[HTTP]状态码详解

请问这段python代码哪里出错了,错误代码:ValueError: invalid literal for int() with base 10: ','?

在哪里放置与 Eloquent 无关的 SQL 查询

javaHashedWheelTimer 使用及源码分析

如何查看sql server 2012是不是激活?在哪里查看?急求呀

没有(Num Bool)的实例来自字面'0'