Java dynamic class loading (java.lang.ClassLoader)
Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті
Это глава из будущей книги «Readings in Java Type System».
---
Вопросы, на которые дам ответы:
1) можно ли загрузить «системный класс» (String, Thread, Class, Object, ClassLoader, ...) by user defined class loader?
2) можно ли организовать повторную загрузку класса?
3) есть ли в Java выгрузка классов?
4) как происходят утечки памяти через загрузку классов у популярных библиотек?
5) анонимные загрузчики классов в Java 7
6) параллельная загрузка классов
7) инструментирование классов при загрузке
.
Пример: исключение при попытке «привести класс к самому себе»:
public class NameSpaceTest { public static void main(String[] args) throws Exception { final String className = NameSpaceTest.class.getName(); final byte[] byteCodes = ClassLoaderUtil.loadByteCode(className); NameSpaceTest ref = (NameSpaceTest) new MyClassLoader() .defineClass(className, byteCodes) .newInstance(); } }>> Exception in thread «main» java.lang.ClassCastException: NameSpaceTest cannot be cast to NameSpaceTest
.
Пример: переполнение PermGen
import static java.lang.String.format; public class OutOfMemoryError_PermGen { public static void main(String[] args) throws Exception { Class<?> clazz = OutOfMemoryError_PermGen.class; byte[] buffer = ClassLoaderUtil.loadByteCode(clazz.getName()); MyClassLoader loader = new MyClassLoader(); for (long index = 0; index < Long.MAX_VALUE; index++) { String newClassName = "_" + format("%0" + (clazz.getSimpleName().length() - 1) + "d", index); byte[] newClassData = new String(buffer, "latin1") .replaceAll(clazz.getSimpleName(), newClassName) .getBytes("latin1"); System.out.println("Count of loaded classes: " + index); loader.defineClass( clazz.getName().replace(clazz.getSimpleName(), newClassName), newClassData); } } }Count of loaded classes: 0
...
Count of loaded classes: 31075
Exception in thread «main» java.lang.OutOfMemoryError: PermGen space
// todo: пересмотреть в виду PermGen->Metaspace в JRE 8 от Oracle.
.
Пример: демонстрация выгрузки классов
import static java.lang.String.format; public class ClassUnloading { public static void main(String[] args) throws Exception { Class<?> clazz = ClassUnloading.class; byte[] buffer = ClassLoaderUtil.loadByteCode(clazz.getName()); MyClassLoader loader; for (long index = 1; index < Long.MAX_VALUE; index++) { String newClassName = "_" + format("%0" + (clazz.getSimpleName().length() - 1) + "d", index); byte[] newClassData = new String(buffer, "latin1") .replaceAll(clazz.getSimpleName(), newClassName) .getBytes("latin1"); loader = new MyClassLoader(); System.out.println("Count of loaded classes: " + index); loader.defineClass( clazz.getName().replace(clazz.getSimpleName(), newClassName), newClassData); } } }Count of loaded classes: 0
...
Count of loaded classes: 100000
...
Count of loaded classes: 1000000
...
Count of loaded classes: 10000000
...
.
Пример: демонстрация наличия отношения делегирования между загрузчиками классов
public interface Root {public Leaf getLeaf();}
public interface Leaf {}
public class LeafImpl implements Leaf {}
public class RootImpl implements Root { private Leaf leaf = new LeafImpl(); @Override public Leaf getLeaf() { return leaf; } }
public class ChainTest { public static void main(String[] args) throws Exception { final String className = RootImpl.class.getName(); final byte[] byteCodes = ClassLoaderUtil.loadByteCode(className); // comparing RootImpl classes Root rootA = (Root) loadClass(className, byteCodes); Root rootB = (Root) loadClass(className, byteCodes); System.out.println(rootA.getClass() == rootB.getClass()); // comparing LeafImpl classes Leaf leafA = rootA.getLeaf(); Leaf leafB = rootB.getLeaf(); System.out.println(leafA.getClass() == leafB.getClass()); } private static Object loadClass(String className, byte[] byteCodes) throws Exception { return new MyClassLoader().defineClass(className, byteCodes).newInstance(); } }false
true
.
Пример: демонстрация наличия кэша загруженных классов
public class ClassCache { public static void main(String[] args) throws Exception { String name = ClassCache.class.getName(); byte[] byteCodes = ClassLoaderUtil.loadByteCode(name); // load - load MyClassLoader loaderLL = new MyClassLoader(); Class<?> clazzLLA = loaderLL.loadClass(name); Class<?> clazzLLB = loaderLL.loadClass(name); System.err.println("load " + (clazzLLA == clazzLLB ? "==" : "!=") + " load"); // load - define MyClassLoader loaderLD = new MyClassLoader(); Class<?> clazzLDA = loaderLD.loadClass(name); Class<?> clazzLDB = loaderLD.defineClass(name, byteCodes); System.err.println("load " + (clazzLDA == clazzLDB ? "==" : "!=") + " define"); // define - load MyClassLoader loaderDL = new MyClassLoader(); Class<?> clazzDLA = loaderDL.defineClass(name, byteCodes); Class<?> clazzDLB = loaderDL.loadClass(name); System.err.println("define " + (clazzDLA == clazzDLB ? "==" : "!=") + " load"); // define - define MyClassLoader loaderDD = new MyClassLoader(); Class<?> clazzDDA = loaderDD.defineClass(name, byteCodes); Class<?> clazzDDB = loaderDD.defineClass(name, byteCodes); System.err.println("define " + (clazzDDA == clazzDDB ? "==" : "!=") + " define"); } }load == load
load != define
define == load
... LinkageError: ... attempted duplicate class definition ...
.
Пример:
.
Утилитарные классы:
public class MyClassLoader extends ClassLoader { public Class<?> defineClass(String name, byte[] byteCodes) { return super.defineClass(name, byteCodes, 0, byteCodes.length); } }
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; public class ClassLoaderUtil { public static byte[] loadByteCode(String className) throws IOException { String fileName = "/" + className.replaceAll("\\.", "/") + ".class"; ByteArrayOutputStream buffer = new ByteArrayOutputStream(); URL url = ClassLoaderUtil.class.getResource(fileName); Files.copy(Paths.get(url.getPath().substring(1)), buffer); return buffer.toByteArray(); } }.
Литература
[DCLinJVM] S. Liang, G. Bracha, «Dynamic Class Loading in the JavaTM Virtual Machine», 1998
10 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів