1+ /*
2+ * SPDX-License-Identifier: Apache-2.0
3+ * Copyright Red Hat Inc. and Hibernate Authors
4+ */
5+ package org .hibernate .testing .memory ;
6+
7+ import org .checkerframework .checker .nullness .qual .Nullable ;
8+
9+ import java .lang .management .ManagementFactory ;
10+ import java .lang .management .ThreadMXBean ;
11+ import java .lang .reflect .Method ;
12+ import java .util .HashMap ;
13+ import java .util .Objects ;
14+
15+ final class HotspotPerThreadAllocationSnapshotter implements MemoryAllocationSnapshotter {
16+
17+ private static final @ Nullable HotspotPerThreadAllocationSnapshotter INSTANCE ;
18+ private static final Method GET_THREAD_ALLOCATED_BYTES ;
19+
20+ static {
21+ ThreadMXBean threadMXBean = ManagementFactory .getThreadMXBean ();
22+ Method method = null ;
23+ try {
24+ @ SuppressWarnings ("unchecked" )
25+ Class <? extends ThreadMXBean > hotspotInterface =
26+ (Class <? extends ThreadMXBean >) Class .forName ( "com.sun.management.ThreadMXBean" );
27+ try {
28+ method = hotspotInterface .getMethod ( "getThreadAllocatedBytes" , long [].class );
29+ }
30+ catch (Exception e ) {
31+ // Ignore
32+ }
33+
34+ if ( !hotspotInterface .isInstance ( threadMXBean ) ) {
35+ threadMXBean = ManagementFactory .getPlatformMXBean ( hotspotInterface );
36+ }
37+ }
38+ catch (Throwable e ) {
39+ // Ignore
40+ }
41+
42+ GET_THREAD_ALLOCATED_BYTES = method ;
43+
44+ HotspotPerThreadAllocationSnapshotter instance = null ;
45+ if ( method != null && threadMXBean != null ) {
46+ try {
47+ instance = new HotspotPerThreadAllocationSnapshotter ( threadMXBean );
48+ instance .snapshot ();
49+ }
50+ catch (Exception e ) {
51+ instance = null ;
52+ }
53+ }
54+ INSTANCE = instance ;
55+ }
56+
57+ public static @ Nullable HotspotPerThreadAllocationSnapshotter getInstance () {
58+ return INSTANCE ;
59+ }
60+
61+ @ Override
62+ public MemoryAllocationSnapshot snapshot () {
63+ long [] threadIds = threadMXBean .getAllThreadIds ();
64+ try {
65+ return new PerThreadMemoryAllocationSnapshot (
66+ threadIds ,
67+ (long []) GET_THREAD_ALLOCATED_BYTES .invoke ( threadMXBean , (Object ) threadIds )
68+ );
69+ }
70+ catch (Exception e ) {
71+ throw new RuntimeException ( e );
72+ }
73+ }
74+
75+ final static class PerThreadMemoryAllocationSnapshot implements MemoryAllocationSnapshot {
76+ private final long [] threadIds ;
77+ private final long [] threadAllocatedBytes ;
78+
79+ PerThreadMemoryAllocationSnapshot (long [] threadIds , long [] threadAllocatedBytes ) {
80+ this .threadIds = threadIds ;
81+ this .threadAllocatedBytes = threadAllocatedBytes ;
82+ }
83+
84+ public long [] threadIds () {
85+ return threadIds ;
86+ }
87+
88+ public long [] threadAllocatedBytes () {
89+ return threadAllocatedBytes ;
90+ }
91+
92+ @ Override
93+ public long difference (MemoryAllocationSnapshot before ) {
94+ final PerThreadMemoryAllocationSnapshot other = (PerThreadMemoryAllocationSnapshot ) before ;
95+ final HashMap <Long , Integer > previousThreadIdToIndexMap = new HashMap <>();
96+ for ( int i = 0 ; i < other .threadIds .length ; i ++ ) {
97+ previousThreadIdToIndexMap .put ( other .threadIds [i ], i );
98+ }
99+ long allocatedBytes = 0 ;
100+ for ( int i = 0 ; i < threadIds .length ; i ++ ) {
101+ allocatedBytes += threadAllocatedBytes [i ];
102+ final Integer previousThreadIndex = previousThreadIdToIndexMap .get ( threadIds [i ] );
103+ if ( previousThreadIndex != null ) {
104+ allocatedBytes -= other .threadAllocatedBytes [previousThreadIndex ];
105+ }
106+ }
107+ return allocatedBytes ;
108+ }
109+
110+ @ Override
111+ public boolean equals (Object obj ) {
112+ if ( obj == this ) {
113+ return true ;
114+ }
115+ if ( obj == null || obj .getClass () != this .getClass () ) {
116+ return false ;
117+ }
118+ var that = (PerThreadMemoryAllocationSnapshot ) obj ;
119+ return Objects .equals ( this .threadIds , that .threadIds ) &&
120+ Objects .equals ( this .threadAllocatedBytes , that .threadAllocatedBytes );
121+ }
122+
123+ @ Override
124+ public int hashCode () {
125+ return Objects .hash ( threadIds , threadAllocatedBytes );
126+ }
127+
128+ @ Override
129+ public String toString () {
130+ return "PerThreadMemoryAllocationSnapshot[" +
131+ "threadIds=" + threadIds + ", " +
132+ "threadAllocatedBytes=" + threadAllocatedBytes + ']' ;
133+ }
134+ }
135+ private final ThreadMXBean threadMXBean ;
136+
137+ HotspotPerThreadAllocationSnapshotter (ThreadMXBean threadMXBean ) {
138+ this .threadMXBean = threadMXBean ;
139+ }
140+
141+ public ThreadMXBean threadMXBean () {
142+ return threadMXBean ;
143+ }
144+
145+ @ Override
146+ public boolean equals (Object obj ) {
147+ if ( obj == this ) {
148+ return true ;
149+ }
150+ if ( obj == null || obj .getClass () != this .getClass () ) {
151+ return false ;
152+ }
153+ var that = (HotspotPerThreadAllocationSnapshotter ) obj ;
154+ return Objects .equals ( this .threadMXBean , that .threadMXBean );
155+ }
156+
157+ @ Override
158+ public int hashCode () {
159+ return Objects .hash ( threadMXBean );
160+ }
161+
162+ @ Override
163+ public String toString () {
164+ return "HotspotPerThreadAllocationSnapshotter[" +
165+ "threadMXBean=" + threadMXBean + ']' ;
166+ }
167+ }
0 commit comments