a topology of memory leaks on the jvm
DESCRIPTION
A topology of memory leaks on the Java virtual machine. This presentation covers all types of memory leaks that can occur on the JVM and categorizes them after the memory region of their occurrence. It further gives some advice on how such leaks can be avoided and presents some debugging techniques.TRANSCRIPT
![Page 1: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/1.jpg)
A topology ofmemory leaks on the JVM
![Page 2: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/2.jpg)
HotSpot JVM process
heap perm gen
native
stack C sta. user
managed
string pool
method area
pc reg.
code cache
young gen
ten. gen
const. pool
eden
sr.1
sr.2
-Xms -Xmx
-Xss -Xsx
-XX:PermSize -XX:MaxPermSize
(operating system)
Java 6 -> 7
Java 7 -> 8
![Page 3: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/3.jpg)
genuine leaks
HotSpot JVM process
heap perm gen
native
stack C sta. user
managed
string pool
method area
pc reg.
code cache
young gen
ten. gen
const. pool
eden
sr.1
sr.2
![Page 4: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/4.jpg)
Field unsafe = Unsafe.class .getDeclaredField("theUnsafe");unsafe.setAccessible(true);Unsafe theUnsafe = (Unsafe) unsafe.get(null);
Allocating native memory with sun.misc.Unsafe1. Getting hold of “the unsafe“
2. Allocating native memory
final int intSize = 4; long arraySize = (1L + Integer.MAX_VALUE) * intSize; long index = unsafe.allocateMemory(arraySize); Random random = new Random(); for(long l = 0L; l < arraySize; l++) unsafe.putInt(random.nextInt());
3. (Hopefully) releasing native memory
// Without me, the memory leaksunsafe.freeMemory(index);
![Page 5: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/5.jpg)
I don‘t do sun.misc.Unsafe
1. But some of your favorite tools do:1. Kryo (used by e.g. Twitter‘s Storm, Apache Hive)2. EhCache‘s “big memory“ (used by e.g. Hibernate)3. JDK (e.g. direct byte buffers)4. General purpose (e.g. GSON, Snappy)
2. Unsafe to become public API in Java 9 (competition with CLR?)
3. JNI leaks have the same effect
4. Java 8 removes “perm gen“ in favor of native memory “meta space“
![Page 6: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/6.jpg)
symptoms of a genuine leakNo exception required by JVMS. However, JVM will be untypical slow when:
1. Creating a thread:Requires allocation of new stacks / pc register.
2. Loading a class / JIT:Requires native memory.
3. Doing I/O:Often requires native memory.
4. Calling a native method:Allocates resources on native stack / user space. Swapping can delayexecution.
5. Garbage collection:Tenured generation collection needs broad access such that OS must swap back the heap. Rule of thump: All JVM heaps must be swapped back into memory. (Leaked memory will stay swapped out.)
![Page 7: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/7.jpg)
Heap leaks
HotSpot JVM process
heap perm gen
native
stack C sta. user
managed
string pool
method area
pc reg.
code cache
young gen
ten. gen
const. pool
eden
sr.1
sr.2
![Page 8: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/8.jpg)
class FixedSizeStack {
private final Object[] objectPool = new Object[10]; private int pointer = -1;
public Object pop() { if(pointer < 0) throw new NoSuchElementException(); return objectPool[pointer--]; }
public Object peek() { if(pointer < 0) throw new NoSuchElementException(); return objectPool[pointer]; }
public void push(Object object) { if(pointer > 8) throw new IllegalStateException("stack overflow"); objectPool[++pointer] = object; } }
![Page 9: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/9.jpg)
Element 5
Element 4
Element 3
Element 2
Element 1
Element 0
Stack
All memory leaks in Java are linked to reference life cycles.
![Page 10: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/10.jpg)
Immutable Stack Element 0
Immutable Stack Element 1
Immutable Stack Element 2
always prefer
immutability
![Page 11: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/11.jpg)
public String substring(int beginIndex, int endIndex) { // omitted argument validation return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); }
String(int offset, int count, char[] value) { this.value = value; this.offset = offset; this.count = count; }
JDK 6
![Page 12: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/12.jpg)
XML, anybody?
org.xml.sax.DocumentHandler { // several callback methods void characters(char[] ch, int start, int length); }
Webpage character blob
DocumentHandler few letter word
is defined by
produces
handles
![Page 13: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/13.jpg)
public String substring(int beginIndex, int endIndex) { // omitted argument validation int subLen = endIndex - beginIndex; return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
public String(char[] value, int offset, int count) { // omitted argument validation this.value = Arrays.copyOfRange(value, offset, offset + count); }
JDK 7
make
defensive
copies instead
of reusing
super sets
![Page 14: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/14.jpg)
interface Message extends Serializable { String getInfo(); }
class ExampleActivity extends Activity { @Override public void onCreate(Bundle bundle) { startService( new Intent(this, getClass()) .putExtra( "foo", new Message() { @Override public String getInfo() { return "bar"; } })); } }
A classic Android leak
![Page 15: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/15.jpg)
class ExampleActivity$1 implements Message {
private ExampleActivity this$0; ExampleActivity$1(ExampleActivity this$0) { this.this$0 = this$0; } @Override public String getInfo() { return "bar"; } }
new Message() { @Override public String getInfo() { return "bar"; } }
uncompiled
desugared
avoid non-
static inner
classes
Non-static inner classes
![Page 16: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/16.jpg)
class Foo { void bar() { List<String> list = Arrays.asList("foo", "bar"); list.forEach(LambdaMetafactory .INVOKEDYNAMIC(Foo::lambda$1) .make()); } private static void lambda$1(String s) { System.out.println(s); } }
class Foo { void bar() { List<String> list = Arrays.asList("foo", "bar"); list.forEach(s -> { System.out.println(s); }); } }
uncompiled
desugared
Java 8 lambda expressions
![Page 17: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/17.jpg)
class Foo { final String prefix; // constructor omitted void bar() { List<String> list = Arrays.asList("foo", "bar"); list.forEach(s -> { System.out.println( prefix + ":" + s) }); } }
uncompiled
desugared
Java 8 lambda expressions
class Foo { final String prefix; // constructor omitted void bar() { List<String> list = Arrays.asList("foo", "bar"); list.forEach(LambdaMetaFactory .INVOKEDYNAMIC(this::lambda$1) .make()); } private void lambda$1(String s) { System.out.println(this.prefix + ":" + s); } }
![Page 18: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/18.jpg)
uncompiled
desugared
class Foo { final String prefix; // constructor omitted void bar() { List<String> list = Arrays.asList("foo", "bar"); String prefix = this.prefix; list.forEach(s -> { System.out.println( prefix + ":" + s)}); } }
class Foo { final String prefix; // constructor omitted void bar() { List<String> list = Arrays.asList("foo", "bar"); String prefix = this.prefix; list.forEach(LambdaMetafactory .INVOKEDYNAMIC(Foo::lambda$1) .make(prefix)); } private static void lambda$1(String prefix, String s) { System.out.println(prefix + ":" + s); } }
pay attention
to your lambda
expression‘s
scope
Java 8 lambda expressions
![Page 19: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/19.jpg)
Other typical causes of heap leaks
• Functional expressions in e.g. Scala / Groovy• Serialization leaks (e.g. Apache Wicket)• Singletons / enums • Context frameworks (e.g. DI like Spring)
![Page 20: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/20.jpg)
Stack leaks
HotSpot JVM process
heap perm gen
native
stack C sta. user
managed
string pool
method area
pc reg.
code cache
young gen
ten. gen
const. pool
eden
sr.1
sr.2
![Page 21: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/21.jpg)
class StackLeak { int SIZE = (int) (0.5 * Runtime .getRuntime() .maxMemory());
void foo() { { byte[] array1 = new byte[SIZE]; } byte[] array2 = new byte[SIZE]; }
void bar() { { byte[] array1 = new byte[SIZE]; } byte[] array2 = new byte[10]; byte[] array3 = new byte[SIZE]; } }
![Page 22: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/22.jpg)
this
void foo() { { byte[] array1 = new byte[SIZE]; } byte[] array2 = new byte[SIZE]; }
array1
SIZEarray1SIZEarray2
local variable array oper
and
stac
k
![Page 23: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/23.jpg)
this
void bar() { { byte[] array1 = new byte[SIZE]; } byte[] array2 = new byte[1]; byte[] array3 = new byte[SIZE]; }
array1array2
SIZEarray11array2SIZEarray3
array3
write short
methods,
mistrust explicit
scopes local variable array oper
and
stac
k
![Page 24: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/24.jpg)
“Perm gen“ leaks
HotSpot JVM process
heap perm gen
native
stack C sta. user
managed
string pool
method area
pc reg.
code cache
young gen
ten. gen
const. pool
eden
sr.1
sr.2
![Page 25: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/25.jpg)
newFoo() Foo.class
getClass()ClassLoader()
getClassLoader()
Field field = ClassLoader.class.getDeclaredField("classes"); field.setAccessible(true); Vector<Class<?>> classes = (Vector<Class<?>>) field.get(ClassLoader.getSystemClassLoader());
classes
Bar.class
Classes and HotSpot
Foo.klass
MA
Bar.klass
![Page 26: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/26.jpg)
ClassLoaders and HotSpot
null
ClassLoader()
ClassLoader()
ClassLoader() ClassLoader()
bootstrap CL(bootstrap directory)
extension CL(extension directory)
system CL(class path)
user CL(explicit)
parent
parent
parent
parent
Foo.classloadClass()
![Page 27: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/27.jpg)
ClassLoader() Thread.class
Foo()Foo.class
Thread()
FooHolder.class
threadLocalMap
class loader leak avoid thread
context
ClassLoader()
class FooHolder { static ThreadLocal<Foo> tlFoo = new ThreadLocal<Foo>(); static { tlFoo.set(new Foo()); } }
![Page 28: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/28.jpg)
Other typical causes of class loader leaks
• Shut down hooks• Use of thread context class loaders (e.g. OSGi)• Service provider interfaces (SPIs)• JDBC drivers (JDK DriverManager)• Security frameworks (e.g. JDK Policy)• Class loader magic (e.g. instrumentation)
avoid redeployment,
use one container
per app
![Page 29: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/29.jpg)
Why do modern applications require so much perm gen memory?
class Foo { public Object bar(Object o) { return o; } }
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Foo.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, doSomethindWith(args)); }}); Foo enhancedFoo = (Foo) enhancer.create();
Created classes: 2 + #methods * 2[FastClass, FastMethod, MethodProxy]
avoid
instrumentation
![Page 30: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/30.jpg)
Modern JDK‘s inflation works similarly
Method
MethodAccessor
DelegatingMethod-AccessorImpl
NativeMethod-AccessorImpl
invoked by
after inflation(byte code generation)
before inflation(JNI)
remember
inflation
![Page 31: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/31.jpg)
Java 8 meta space
• Permanent generation rebranded (and probably approximation to JRockit)
• Meta space is allocated in native memory• No space limit by default (MaxMetaspaceSize)• Today‘s debugging tools will not be able to
read meta space
![Page 32: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/32.jpg)
Tenuring leaks
HotSpot JVM process
heap perm gen
native
stack C sta. user
managed
string pool
method area
pc reg.
code cache
young gen
ten. gen
const. pool
eden
sr.1
sr.2
![Page 33: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/33.jpg)
public void foo() { Set<Bar> bars = new HashSet<Bar>(); for(int i = 0; i < 100; i++) { for(int j = 0; j < 100; j++) { bars.add(makeBar(i, j)); } doSomethingWith(bars); bars.clear(); } }
public void foo() { for(int i = 0; i < 100; i++) { Set<Bar> bars = new HashSet<Bar>(); for(int j = 0; j < 100; j++) { bars.add(makeBar(i, j)); } doSomethingWith(bars); } }
young generation
Tenured generation
eden survivor 1 survivor 2
bars bars bars bars
collection of survivor 2collection of survivor 1
don‘t be scared of
“new“,
avoid object
pooling,
JIT knows best
![Page 34: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/34.jpg)
Implicit memory leaks
• Leaked threads: Require fixed amount of memory for call stacks / pc register / general allocation
• Leaked handles (files, sockets, databases): Usually require JVM memory resources
![Page 35: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/35.jpg)
debugging / profilingjmap -dump:live,format=b,file=<...> <pid>
java -XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath=<...> <...>
jhat <hprof file>select a from [I a where a.length >= 256
GUI tools: MAT (Eclipse RCP) / JVisualVM / HeapWalker
tools that create heap
dumps by
instrumentation
require memory to
run
![Page 36: A topology of memory leaks on the JVM](https://reader036.vdocuments.net/reader036/viewer/2022062501/554f6bc3b4c905c8088b5224/html5/thumbnails/36.jpg)
http://rafael.codes@rafaelcodes
http://documents4j.comhttps://github.com/documents4j/documents4j
http://bytebuddy.nethttps://github.com/raphw/byte-buddy