Skip to content

jcmd jmap

ceragon edited this page Jun 16, 2022 · 2 revisions

jmap

显示样例

$ jhsdb jmap --heap --pid 31556

JVM version is 11.0.4+11-LTS

using thread-local object allocation.
Garbage-First (G1) GC with 13 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1363144 (1.2999954223632812MB)
   MaxNewSize               = 1287651328 (1228.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 528482304 (504.0MB)
   MaxMetaspaceSize         = 536870912 (512.0MB)
   G1HeapRegionSize         = 4194304 (4.0MB)

Heap Usage:
G1 Heap:
   regions  = 512
   capacity = 2147483648 (2048.0MB)
   used     = 645041952 (615.1599426269531MB)
   free     = 1502441696 (1432.8400573730469MB)
   30.037106573581696% used
G1 Young Generation:
Eden Space:
   regions  = 4
   capacity = 29360128 (28.0MB)
   used     = 16777216 (16.0MB)
   free     = 12582912 (12.0MB)
   57.142857142857146% used
Survivor Space:
   regions  = 1
   capacity = 4194304 (4.0MB)
   used     = 4194304 (4.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 150
   capacity = 629145600 (600.0MB)
   used     = 624070432 (595.1599426269531MB)
   free     = 5075168 (4.840057373046875MB)
   99.19332377115886% used
$ ./jhsdb jmap --histo --pid 15532

JVM version is 11.0.4+11-LTS
Iterating over heap. This may take a while...
Object Histogram:

num       #instances    #bytes  Class description
--------------------------------------------------------------------------
1:              50053   9540128 byte[]
2:              9645    3826408 char[]
3:              14071   1163648 int[]
4:              18019   915568  java.lang.Object[]
5:              19258   462192  java.lang.String
6:              3854    456280  java.lang.Class
7:              6481    311088  java.util.HashMap
8:              8605    275360  java.util.concurrent.ConcurrentHashMap$Node
9:              2975    238000  sun.rmi.transport.ConnectionInputStream
10:             5950    238000  java.io.ObjectOutputStream$HandleTable
11:             2975    214200  sun.rmi.transport.ConnectionOutputStream
12:             13243   211888  java.lang.Object
13:             6010    192320  java.rmi.server.UID
14:             5624    179968  java.util.HashMap$Node
Total :         247963  20974000
Heap traversal took 3.763 seconds.

显示源码

public class SALauncher {
    public static void main(String[] args) {
        String[] oldArgs = Arrays.copyOfRange(args, 1, args.length);
        if (args[0].equals("jmap")) {
            runJMAP(oldArgs);
            return;
        }
    }

    private static void runJMAP(String[] oldArgs) {
        SAGetopt sg = new SAGetopt(oldArgs);
        String[] longOpts = {"exe=", "core=", "pid=",
                "heap", "binaryheap", "dumpfile=", "histo", "clstats", "finalizerinfo"};
        ArrayList<String> newArgs = new ArrayList();
        String pid = null;
        while ((s = sg.next(null, longOpts)) != null) {
            if (s.equals("pid")) {
                pid = sg.getOptarg();
                continue;
            }
            if (s.equals("heap")) {
                newArgs.add("-heap");
                continue;
            }
        }
        buildAttachArgs(newArgs, pid, exe, core, false);
        JMap.main(newArgs.toArray(new String[newArgs.size()]));
    }
}
public class JMap extends Tool {
    public static void main(String[] args) {
        int mode = MODE_PMAP;
        if (args.length > 1) {
            if (modeFlag.equals("-histo")) {
                mode = MODE_HISTOGRAM;
            }
        }
        JMap jmap = new JMap(mode);
        jmap.execute(args);
    }
}
public abstract class Tool implements Runnable {
    private int debugeeType;

    protected void execute(String[] args) {
        returnStatus = start(args);
    }

    private int start(String[] args) {
        int pid = 0;
        switch (args.length) {
            case 1:
                pid = Integer.parseInt(args[0]);
                debugeeType = DEBUGEE_PID;
                break;
        }
        agent = new HotSpotAgent();
        switch (debugeeType) {
            case DEBUGEE_PID:
                out.println("Attaching to process ID " + pid + ", please wait...");
                agent.attach(pid);
                break;
        }
        out.println("Debugger attached successfully.");
        startInternal();
        return 0;
    }

    private void startInternal() {
        PrintStream out = System.out;
        VM vm = VM.getVM();
        if (vm.isServerCompiler()) {
            out.println("Server compiler detected.");
        }
        String version = vm.getVMRelease();
        if (version != null) {
            out.print("JVM version is ");
            out.println(version);
        }
        run();
    }
}
public class JMap extends Tool {
    public void run() {
        Tool tool = null;
        switch (mode) {
            case MODE_HISTOGRAM:
                tool = new ObjectHistogram();
                break;
        }
        tool.setAgent(getAgent());
        tool.setDebugeeType(getDebugeeType());
        tool.run();
    }
}
public class ObjectHistogram extends Tool {
    public void run() {
        run(System.out, System.err);
    }

    public void run(PrintStream out, PrintStream err) {
        ObjectHeap heap = VM.getVM().getObjectHeap();
        sun.jvm.hotspot.oops.ObjectHistogram histogram = new sun.jvm.hotspot.oops.ObjectHistogram();
        err.println("Iterating over heap. This may take a while...");
        long startTime = System.currentTimeMillis();
        heap.iterate(histogram);
        long endTime = System.currentTimeMillis();
        histogram.printOn(out);
        float secs = (float) (endTime - startTime) / 1000.0f;
        err.println("Heap traversal took " + secs + " seconds.");
    }
}

heap.iterate()

public class ObjectHeap {
    public void iterate(HeapVisitor visitor) {
        iterateLiveRegions(collectLiveRegions(), visitor, null);
    }

    private List collectLiveRegions() {
        // 主体逻辑代码
    }

    private void iterateLiveRegions(List liveRegions, HeapVisitor visitor, ObjectFilter of) {
        long totalSize = 0;
        for (int i = 0; i < liveRegions.size(); i += 2) {
            Address bottom = (Address) liveRegions.get(i);
            Address top = (Address) liveRegions.get(i + 1);
            totalSize += top.minus(bottom);
        }
        visitor.prologue(totalSize);

        for (int i = 0; i < liveRegions.size(); i += 2) {
            Address bottom = (Address) liveRegions.get(i);
            Address top = (Address) liveRegions.get(i + 1);
            OopHandle handle = bottom.addOffsetToAsOopHandle(0);
            while (handle.lessThan(top)) {
                Oop obj = null;
                obj = newOop(handle);
                if (of == null || of.canInclude(obj)) {
                    if (visitor.doObj(obj)) {
                        // doObj() returns true to abort this loop.
                        break;
                    }
                }
                handle = handle.addOffsetToAsOopHandle(obj.getObjectSize());
            }
        }
        visitor.epilogue();
    }
}
public class ObjectHistogram implements HeapVisitor {
    private HashMap map = new HashMap();

    public void prologue(long size) {
    }

    public boolean doObj(Oop obj) {
        Klass klass = obj.getKlass();
        // 不存在就创建
        if (!map.containsKey(klass)) map.put(klass, new ObjectHistogramElement(klass));
        // 增加对象数量和总占用大小
        ((ObjectHistogramElement) map.get(klass)).updateWith(obj);
        return false;
    }

    public void epilogue() {
    }
}
public class ObjectHistogramElement {

    private Klass klass;
    private long count; // Number of instances of klass
    private long size;  // Total size of all these instances

    public ObjectHistogramElement(Klass k) {
        klass = k;
        count = 0;
        size = 0;
    }

    public void updateWith(Oop obj) {
        count = count + 1;
        size = size + obj.getObjectSize();
    }
}

histogram.printOn()

public class ObjectHistogram implements HeapVisitor {
    public void printOn(PrintStream tty) {
        List list = getElements();
        ObjectHistogramElement.titleOn(tty);
        Iterator iterator = list.listIterator();
        int num = 0;
        int totalCount = 0;
        int totalSize = 0;
        while (iterator.hasNext()) {
            ObjectHistogramElement el = (ObjectHistogramElement) iterator.next();
            num++;
            totalCount += el.getCount();
            totalSize += el.getSize();
            tty.print(num + ":" + "\t\t");
            el.printOn(tty);
        }
        tty.println("Total : " + "\t" + totalCount + "\t" + totalSize);
    }

    public List getElements() {
        List list = new ArrayList();
        list.addAll(map.values());
        Collections.sort(list, new Comparator() {
            public int compare(Object o1, Object o2) {
                return ((ObjectHistogramElement) o1).compare((ObjectHistogramElement) o2);
            }
        });
        return list;
    }
}
public class ObjectHistogramElement {
    public static void titleOn(PrintStream tty) {
        tty.println("Object Histogram:");
        tty.println();
        tty.println("num " + "\t" + "  #instances" + "\t" + "#bytes" + "\t" + "Class description");
        tty.println("--------------------------------------------------------------------------");
    }

    public void printOn(PrintStream tty) {
        tty.print(count + "\t" + size + "\t");
        tty.print(getDescription());
        tty.println();
    }
}

collectLiveRegions

结构体

public class Oop {
    private OopHandle handle;

    // 获取 vm 中类的信息
    public Klass getKlass() {
        if (VM.getVM().isCompressedKlassPointersEnabled()) {
            return (Klass) compressedKlass.getValue(getHandle());
        } else {
            return (Klass) klass.getValue(getHandle());
        }
    }

    // 计算对象大小
    public long getObjectSize() {
        Klass k = getKlass();
        return ((InstanceKlass) k).getObjectSize(this);
    }
}
public class ObjectHeap {
    private List collectLiveRegions() {
        List liveRegions = new ArrayList();
        LiveRegionsCollector lrc = new LiveRegionsCollector(liveRegions);

        CollectedHeap heap = VM.getVM().getUniverse().heap();
        if (heap instanceof G1CollectedHeap) {
            G1CollectedHeap g1h = (G1CollectedHeap) heap;
            g1h.heapRegionIterate(lrc);
        }

        if (VM.getVM().getUseTLAB()) {
            for (JavaThread thread = VM.getVM().getThreads().first(); thread != null;
                 thread = thread.next()) {
                ThreadLocalAllocBuffer tlab = thread.tlab();
                if (tlab.start() != null) {
                    liveRegions.add(tlab.start());
                    liveRegions.add(tlab.start());
                    liveRegions.add(tlab.top());
                    liveRegions.add(tlab.hardEnd());
                }
            }
        }
        sortLiveRegions(liveRegions);
        return liveRegions;
    }
}

VM.heap()

public class Universe {
    private static AddressField collectedHeapField;
    private static VirtualConstructor heapConstructor;

    private static synchronized void initialize(TypeDataBase db) {
        collectedHeapField = type.getAddressField("_collectedHeap");
        heapConstructor = new VirtualConstructor(db);
        addHeapTypeIfInDB(db, G1CollectedHeap.class);
    }

    public CollectedHeap heap() {
        return (CollectedHeap) heapConstructor.instantiateWrapperFor(collectedHeapField.getValue());
    }
}
public class VirtualConstructor extends InstanceConstructor<VMObject> {
    private TypeDataBase db;
    private Map map;

    public VMObject instantiateWrapperFor(Address addr) throws WrongTypeException {
        for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
            String typeName = (String) iter.next();
            if (db.addressTypeIsEqualToType(addr, db.lookupType(typeName))) {
                return (VMObject) VMObjectFactory.newObject((Class) map.get(typeName), addr);
            }
        }
    }
}
public class G1CollectedHeap extends CollectedHeap {
    public G1CollectedHeap(Address addr) {
        super(addr);
    }
}

g1 heapRegionIterate

public class G1CollectedHeap extends CollectedHeap {
    // HeapRegionManager _hrm;
    static private long hrmFieldOffset;

    protected Address addr;

    public void heapRegionIterate(SpaceClosure scl) {
        Iterator<HeapRegion> iter = heapRegionIterator();
        while (iter.hasNext()) {
            HeapRegion hr = iter.next();
            scl.doSpace(hr);
        }
    }

    private Iterator<HeapRegion> heapRegionIterator() {
        return hrm().heapRegionIterator();
    }

    public HeapRegionManager hrm() {
        Address hrmAddr = addr.addOffsetTo(hrmFieldOffset);
        return (HeapRegionManager) VMObjectFactory.newObject(HeapRegionManager.class,
                hrmAddr);
    }
}
public class HeapRegionManager extends VMObject {
    protected Address addr;
    // G1HeapRegionTable _regions
    static private long regionsFieldOffset;

    public HeapRegionManager(Address addr) {
        super(addr);
    }

    public Iterator<HeapRegion> heapRegionIterator() {
        return regions().heapRegionIterator(length());
    }

    private G1HeapRegionTable regions() {
        Address regionsAddr = addr.addOffsetTo(regionsFieldOffset);
        return (G1HeapRegionTable) VMObjectFactory.newObject(G1HeapRegionTable.class, regionsAddr);
    }

    public long length() {
        return regions().length();
    }
}
public class G1HeapRegionTable extends VMObject {
    // HeapRegion** _base;
    static private AddressField baseField;
    // uint _length;
    static private CIntegerField lengthField;

    public G1HeapRegionTable(Address addr) {
        super(addr);
    }

    public Iterator<HeapRegion> heapRegionIterator(long committedLength) {
        return new HeapRegionIterator(committedLength);
    }

    public long length() {
        return lengthField.getValue(addr);
    }


    private class HeapRegionIterator implements Iterator<HeapRegion> {
        private long index;
        private long length;
        private HeapRegion next;

        HeapRegionIterator(long totalLength) {
            index = 0;
            length = totalLength;
            positionToNext();
        }

        public HeapRegion positionToNext() {
            HeapRegion result = next;
            while (index < length && at(index) == null) {
                index++;
            }
            if (index < length) {
                next = at(index);
                index++; // restart search at next element
            } else {
                next = null;
            }
            return result;
        }

        @Override
        public HeapRegion next() {
            return positionToNext();
        }
    }

    private HeapRegion at(long index) {
        Address arrayAddr = baseField.getValue(addr);
        // Offset of &_base[index]
        long offset = index * VM.getVM().getAddressSize();
        Address regionAddr = arrayAddr.getAddressAt(offset);
        return (HeapRegion) VMObjectFactory.newObject(HeapRegion.class,
                regionAddr);
    }
}
public class HeapRegion extends CompactibleSpace {
    private static AddressField bottomField;
    static private AddressField topField;
    private static long typeFieldOffset;
    private HeapRegionType type;

    public HeapRegion(Address addr) {
        super(addr);
        Address typeAddr = (addr instanceof OopHandle) ? addr.addOffsetToAsOopHandle(typeFieldOffset)
                : addr.addOffsetTo(typeFieldOffset);
        type = (HeapRegionType) VMObjectFactory.newObject(HeapRegionType.class, typeAddr);
    }

    @Override
    public List getLiveRegions() {
        List res = new ArrayList();
        res.add(new MemRegion(bottom(), top()));
        return res;
    }

    public Address bottom() {
        return bottomField.getValue(addr);
    }

    public Address top() {
        return topField.getValue(addr);
    }
}

newOop

public class ObjectHeap {
    public Oop newOop(OopHandle handle) {
        Klass klass = Oop.getKlassForOopHandle(handle);
        if (klass != null) {
            if (klass instanceof TypeArrayKlass) return new TypeArray(handle, this);
            if (klass instanceof ObjArrayKlass) return new ObjArray(handle, this);
            if (klass instanceof InstanceKlass) return new Instance(handle, this);
        }
    }
}
public class Oop {
    private static CIntField mark;
    private static MetadataField  klass;
    private static NarrowKlassField compressedKlass;
    public static Klass getKlassForOopHandle(OopHandle handle) {
        if (VM.getVM().isCompressedKlassPointersEnabled()) {
            return (Klass)Metadata.instantiateWrapperFor(handle.getCompKlassAddressAt(compressedKlass.getOffset()));
        } else {
            return (Klass)Metadata.instantiateWrapperFor(handle.getAddressAt(klass.getOffset()));
        }
    }
}
abstract public class Metadata extends VMObject {
    private static VirtualBaseConstructor<Metadata> metadataConstructor;
    private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
        metadataConstructor.addMapping("Klass", Klass.class);
    }
    public static Metadata instantiateWrapperFor(Address addr) {
        return metadataConstructor.instantiateWrapperFor(addr);
    }
}
public class Klass extends Metadata implements ClassConstants {
    public Klass(Address addr) {
        super(addr);
    }
}

Clone this wiki locally