que documentar codigo atomicas java classloader dynamic-class-loaders dynamic-class-creation

documentar - java.lang que es



¿Cómo puedo modificar una clase java.lang sobre la marcha? (8)

Estoy buscando una manera de agregar campos a un Hilo sobre la marcha reescribiendo el código de byte y recargando la clase, sin estar seguro de que sea posible. Cualquier puntero de bienvenida. Encontré algo de información sobre cómo modificar y cargar una clase, y sé que JRebel puede intercambiar su código a la perfección, pero no estoy seguro de si se aplica el mismo enfoque / herramientas aquí.

La motivación aquí es explorar una alternativa teóricamente mejor para enhebrar objetos locales. Si el método funciona, debería poder reemplazar el subproceso local por una anotación y el resultado debería superar la implementación actual de JDK.

PD: Por favor, sálvame la "raíz de todo mal discurso"

Caso de uso aclaratorio:

Imagina que tengo una clase con un ThreadLocal:

class A { ThreadLocal<Counter> counter; ... counter.get().inc() }

Me gustaría reemplazar eso con una anotación:

class A { @ThreadLocal Counter counter; ... counter.inc() }

Pero en lugar de que se genere el código anterior, me gustaría mutar Thread, de modo que Thread ahora tendrá un campo Acounter y el código real será:

class A { // Nothing here, field is now in Thread ... Thread.currentThread().Acounter.inc() }


En la actualidad, es imposible redefinir una clase en tiempo de ejecución, de manera que la redefinición dará como resultado nuevos métodos o campos. Esto se debe a la complejidad involucrada en la exploración del montón para todas las instancias existentes y su transformación + sus referencias + posibles actualizadores de base de desplazamiento de campo no seguros (como AtomicFieldUpdater).

Esta limitación puede eliminarse como parte del JEP-159 pero como se discutió en el grupo de correo de interés de concurrencia, este es un cambio de gran impacto, por lo que nunca puede suceder.

El uso de Javaassist / similar permitirá la transformación de una clase a una nueva clase con nuevos métodos / campos. Esta clase puede ser cargada por un ClassLoader y utilizada en tiempo de ejecución, pero su definición no reemplazará las instancias existentes. Por lo tanto, no será posible usar este método combinado con un agente para redefinir la clase como una instrumentación. La redefinición está limitada de tal manera que: "La redefinición puede cambiar los cuerpos de los métodos, la combinación constante y los atributos. La redefinición no debe agregar, eliminar o cambiar el nombre campos ... "ver here .

Así que por ahora, NO.



He visto una solución de carga de clases personalizada que recargó JAR dinámicamente: usted define un ClassLoader por archivo JAR y lo usa para cargar las clases de ese JAR; para volver a cargar todo el JAR, simplemente "mata" su instancia de ClassLoader y crea otra (después de reemplazar el archivo JAR).

No creo que sea posible modificar la clase Thread interna de Java de esta manera porque no tienes control sobre System ClassLoader . Una posible solución es tener una clase CustomThreadWeaver que genere una nueva clase que extienda Thread con las variables que necesita y use un DynamicWeavedThreadClassLoader personalizado para cargarlas.

Buena suerte y muéstranos tu monstruo cuando tengas éxito ;-)


Lo que estás intentando hacer no es posible.

Como ya sabe sobre ThreadLocal, ya sabe cuál es la solución sugerida.

Alternativamente, puedes sub-clasificar Thread y agregar tus propios campos; sin embargo, solo aquellos subprocesos que creas explícitamente de esa clase tendrán esos campos, por lo que aún tendrás que poder "retroceder" para usar un subproceso local.

La verdadera pregunta es "¿por qué?", ​​Como en "¿por qué un subproceso local es insuficiente para sus requisitos?"


Para hacer lo que quieras, la alternativa más simple sería usar una subclase de Hilo, ejecutarlo y luego, dentro de ese hilo, ejecuta el código de tu ejemplo (junto con una conversión de currentThread () a tu subclase).



Si desea cambiar el comportamiento de "clase" en tiempo de ejecución, puede probar javassist . API está here


Posible uso de instrumentación y posiblemente bibliotecas como javassist para modificar el código sobre la marcha. (sin embargo, actualmente no es posible agregar y eliminar campos, métodos o constructores)

//Modify code using javassist and call CtClass#toBytecode() or load bytecode from file byte[] nevcode; Class<?> clz = Class.forName("any.class.Example"); instrumentationInstace.redefineClasses(new ClassDefinition(clz, nevcode));

No olvide agregar Can-Redefine-Classes: true al manifiesto de su agente de Java.

Ejemplo real: optimización de java <9 string.replace(CharSequence, CharSequence) usando javassist:

String replace_src = "{String str_obj = this;/n" + "char[] str = this.value;/n" + "String find_obj = $1.toString();/n" + "char[] find = find_obj.value;/n" + "String repl_obj = $2.toString();/n" + "char[] repl = repl_obj.value;/n" + "/n" + "if(str.length == 0 || find.length == 0 || find.length > str.length) {/n" + " return str_obj;/n" + "}/n" + "int start = 0;/n" + "int end = str_obj.indexOf(find_obj, start);/n" + "if(end == -1) {/n" + " return str_obj;/n" + "}/n" + "int inc = repl.length - find.length;/n" + "int inc2 = str.length / find.length / 512;/ninc2 = ((inc2 < 16) ? 16 : inc);/n" + "int sb_len = str.length + ((inc < 0) ? 0 : (inc * inc2));/n" + "StringBuilder sb = (sb_len < 0) ? new StringBuilder(str.length) : new StringBuilder(sb_len);/n" + "while(end != -1) {/n" + " sb.append(str, start, end - start);/n" + " sb.append(repl);/n" + " start = end + find.length;/n" + " end = str_obj.indexOf(find_obj, start);/n" + "}/n" + "if(start != str.length) {/n" + " sb.append(str, start, str.length - start);/n" + "}/n" + "return sb.toString();/n" +"}"; ClassPool cp = new ClassPool(true); CtClass clz = cp.get("java.lang.String"); CtClass charseq = cp.get("java.lang.CharSequence"); clz.getDeclaredMethod("replace", new CtClass[] { charseq, charseq }).setBody(replace_src); instrumentationInstance.redefineClasses(new ClassDefinition(Class.forName(clz.getName(), false, null), clz.toBytecode()));