Back to Repositories

Validating VmTool Memory Management and Thread Control in Alibaba Arthas

This test suite validates the VmTool functionality in Arthas, focusing on JVM memory inspection, instance management, and thread control capabilities. The tests verify core features like instance counting, memory size calculation, and thread interruption.

Test Coverage Overview

The test suite provides comprehensive coverage of VmTool’s core functionalities:
  • Instance management and memory analysis
  • Class loading inspection
  • Thread interruption handling
  • Memory leak detection scenarios
  • Interface implementation testing
Tests include boundary conditions and performance measurements for memory-intensive operations.

Implementation Analysis

The testing approach employs JUnit framework with systematic validation of native JVM operations. Tests utilize WeakReferences and atomic operations to ensure thread-safety and proper garbage collection behavior.

Implementation includes performance benchmarking patterns and memory leak detection through iterative instance creation and disposal.

Technical Details

Testing infrastructure includes:
  • JUnit 4 test framework
  • AssertJ for fluent assertions
  • Custom VmTool native library integration
  • JVM configuration with -Xms128m -Xmx128m
  • JProfiler integration support for memory analysis

Best Practices Demonstrated

The test suite exemplifies robust testing practices through:
  • Systematic setup and teardown procedures
  • Comprehensive error handling and validation
  • Performance measurement with atomic counters
  • Memory leak prevention checks
  • Interface and implementation testing separation

alibaba/arthas

arthas-vmtool/src/test/java/arthas/VmToolTest.java

            
package arthas;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.Test;

import com.taobao.arthas.common.VmToolUtils;

/**
 * 以下本地测试的jvm参数均为:-Xms128m -Xmx128m
 */
public class VmToolTest {
    private VmTool initVmTool() {
        File path = new File(VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile();

        String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath();
        return VmTool.getInstance(libPath);
    }

    /**
     * macbook上运行结果如下
     * allLoadedClasses->1050
     * arthas.VmTool@5bb21b69 arthas.VmTool@6b9651f3
     * before instances->[arthas.VmTool@5bb21b69, arthas.VmTool@6b9651f3]
     * size->16
     * count->2
     * sum size->32
     * null null
     * after instances->[]
     */
    @Test
    public void testIsSnapshot() {
        try {
            VmTool vmtool = initVmTool();
            //调用native方法,获取已加载的类,不包括小类型(如int)
            Class<?>[] allLoadedClasses = vmtool.getAllLoadedClasses();
            System.out.println("allLoadedClasses->" + allLoadedClasses.length);

            //通过下面的例子,可以看到getInstances(Class<T> klass)拿到的是当前存活的所有对象
            WeakReference<VmToolTest> weakReference1 = new WeakReference<VmToolTest>(new VmToolTest());
            WeakReference<VmToolTest> weakReference2 = new WeakReference<VmToolTest>(new VmToolTest());
            System.out.println(weakReference1.get() + " " + weakReference2.get());
            VmTool[] beforeInstances = vmtool.getInstances(VmTool.class);
            System.out.println("before instances->" + beforeInstances);
            System.out.println("size->" + vmtool.getInstanceSize(weakReference1.get()));
            System.out.println("count->" + vmtool.countInstances(VmTool.class));
            System.out.println("sum size->" + vmtool.sumInstanceSize(VmTool.class));
            beforeInstances = null;

            vmtool.forceGc();
            Thread.sleep(100);
            System.out.println(weakReference1.get() + " " + weakReference2.get());
            VmTool[] afterInstances = vmtool.getInstances(VmTool.class);
            System.out.println("after instances->" + afterInstances);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testGetInstancesMemoryLeak() {
        //这里睡20s是为了方便用jprofiler连接上进程
//        try {
//            Thread.sleep(20000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        VmTool vmtool = initVmTool();
        final AtomicLong totalTime = new AtomicLong();
        //本地测试请改成200000
        for (int i = 1; i <= 2; i++) {
            long start = System.currentTimeMillis();
            WeakReference<Object[]> reference = new WeakReference<Object[]>(vmtool.getInstances(Object.class));
            Object[] instances = reference.get();
            long cost = System.currentTimeMillis() - start;
            totalTime.addAndGet(cost);
            System.out.println(i + " instance size:" + (instances == null ? 0 : instances.length) + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
            instances = null;
            vmtool.forceGc();
        }
    }

    @Test
    public void testSumInstancesMemoryLeak() {
        //这里睡20s是为了方便用jprofiler连接上进程
//        try {
//            Thread.sleep(20000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        VmTool vmtool = initVmTool();
        final AtomicLong totalTime = new AtomicLong();
        //本地测试请改成200000
        for (int i = 1; i <= 2; i++) {
            long start = System.currentTimeMillis();
            long sum = vmtool.sumInstanceSize(Object.class);
            long cost = System.currentTimeMillis() - start;
            totalTime.addAndGet(cost);
            System.out.println(i + " sum:" + sum + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
        }
    }

    @Test
    public void testCountInstancesMemoryLeak() {
        //这里睡20s是为了方便用jprofiler连接上进程
//        try {
//            Thread.sleep(20000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        VmTool vmtool = initVmTool();
        final AtomicLong totalTime = new AtomicLong();
        //本地测试请改成200000
        for (int i = 1; i <= 2; i++) {
            long start = System.currentTimeMillis();
            long count = vmtool.countInstances(Object.class);
            long cost = System.currentTimeMillis() - start;
            totalTime.addAndGet(cost);
            System.out.println(i + " count:" + count + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
        }
    }

    @Test
    public void testGetAllLoadedClassesMemoryLeak() {
        //这里睡20s是为了方便用jprofiler连接上进程
//        try {
//            Thread.sleep(20000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        VmTool vmtool = initVmTool();
        final AtomicLong totalTime = new AtomicLong();
        //本地测试请改成200000
        for (int i = 1; i <= 2; i++) {
            long start = System.currentTimeMillis();
            Class<?>[] allLoadedClasses = vmtool.getAllLoadedClasses();
            long cost = System.currentTimeMillis() - start;
            totalTime.addAndGet(cost);
            System.out.println(i + " class size:" + allLoadedClasses.length + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
            allLoadedClasses = null;
        }
    }

    class LimitTest {
    }

    @Test
    public void test_getInstances_lmiit() {
        VmTool vmtool = initVmTool();

        ArrayList<LimitTest> list = new ArrayList<LimitTest>();
        for (int i = 0; i < 10; ++i) {
            list.add(new LimitTest());
        }
        LimitTest[] instances = vmtool.getInstances(LimitTest.class, 5);
        Assertions.assertThat(instances).hasSize(5);
        LimitTest[] instances2 = vmtool.getInstances(LimitTest.class, -1);
        Assertions.assertThat(instances2).hasSize(10);
        LimitTest[] instances3 = vmtool.getInstances(LimitTest.class, 1);
        Assertions.assertThat(instances3).hasSize(1);
    }

    interface III {
    }

    class AAA implements III {
    }

    @Test
    public void test_getInstances_interface() {
        AAA aaa = new AAA();
        VmTool vmtool = initVmTool();
        III[] interfaceInstances = vmtool.getInstances(III.class);
        Assertions.assertThat(interfaceInstances.length).isEqualTo(1);

        AAA[] ObjectInstances = vmtool.getInstances(AAA.class);
        Assertions.assertThat(ObjectInstances.length).isEqualTo(1);

        Assertions.assertThat(interfaceInstances[0]).isEqualTo(ObjectInstances[0]);
    }

    @Test
    public void test_interrupt_thread() throws InterruptedException {
        String threadName = "interruptMe";
        final RuntimeException[] re = new RuntimeException[1];
        Runnable runnable = new Runnable() {
            @Override public void run() {
                try {
                    System.out.printf("Thread name is: [%s], thread id is: [%d].\n", Thread.currentThread().getName(),Thread.currentThread().getId());
                    TimeUnit.SECONDS.sleep(1000);
                } catch (InterruptedException e) {
                    re[0] = new RuntimeException("interrupted " + Thread.currentThread().getId() + " thread success.");
                }
            }
        };
        Thread interruptMe = new Thread(runnable,threadName);
        Thread interruptMe1 = new Thread(runnable,threadName);

        interruptMe.start();
        interruptMe1.start();

        VmTool tool = initVmTool();
        tool.interruptSpecialThread((int) interruptMe.getId());
        TimeUnit.SECONDS.sleep(5);
        Assert.assertEquals(("interrupted " + interruptMe.getId() + " thread success."), re[0].getMessage());
    }
}