16

Я ищу способ добавления полей в поток на лету, переписывая байтовый код и перезагружая класс, не будучи уверенным, что это вообще возможно. Любые указатели приветствуются. Я нашел некоторую информацию о модификации и загрузке класса, и я знаю, что JRebel может плавно перетаскивать ваш код, но не уверен, что здесь применяются одни и те же подходы/инструменты.Как я могу изменить класс java.lang на лету?

Мотивация здесь исследует теоретически лучшую альтернативу потоковым локальным объектам. Если этот метод работает, я должен иметь возможность заменить thread local аннотацией, и результат должен превзойти текущую реализацию JDK.

PS: Пожалуйста, сохраните мне "корень всех зол речи"

Разъяснение использования:

Представьте У меня есть класс с ThreadLocal:


class A { 
    ThreadLocal&ltCounter> counter; 
    ... 
    counter.get().inc() 
} 

Я хотел бы замените это аннотацией:


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

Но вместо приведенного выше кода получение гена рейтинг Я хотел бы мутировать Тему такими, что тема теперь будет иметь поле Acounter и фактический код будет:


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

вы бы добавить, что вы в настоящее время или работали на ..следующие код, чтобы мы могли лучше помочь вам, пожалуйста? –

+0

Прошу прощения, это на предварительном этапе исследования, на данный момент кода нет. Я отредактирую Q, чтобы уточнить намерение. –

ответ

10

В настоящее время невозможно переопределить класс во время выполнения таким образом, что переопределение приведет к новым методам и полям. Это связано с сложностью, связанной с сканированием кучи для всех существующих экземпляров и их преобразованием + их ссылками + потенциалом. Невосприимчивые к обновлению базовых полей (например, AtomicFieldUpdater).

Это ограничение может быть снято как часть JEP-159, но, как обсуждалось в группе рассылки для совместной работы с процентами, это большое изменение воздействия, поэтому этого никогда не может быть.

Использование Javaassist/аналогичное позволит преобразовать класс в новый класс с новыми методами/полями. Этот класс может быть загружен ClassLoader и использоваться во время выполнения, но это определение не заменит существующие экземпляры. Таким образом, невозможно будет использовать этот метод в сочетании с агентом, чтобы переопределить класс, поскольку переопределение инструментария ограничено, так что: «переопределение может изменить тела методов, пул констант и атрибуты. Переопределение не должно добавлять, удалять или переименовывать поля ... "см. here.

Итак, пока что нет.

5

Если вы хотите изменить поведение «класса» во время выполнения, вы можете попробовать javassist , API here

+0

Я ищу способ изменить классы, которые уже загружены и выделены. Я могу найти достаточно документации для поддержки изменения класса по мере его загрузки, но не для того, чтобы заменить текущий поток моей новой версией. –

4

Я видел пользовательское решение для загрузки классов, которое динамически перезагружает JAR - вы определяете один ClassLoader на JAR-файл и используете его для загрузки классов из этого JAR; для перезагрузки всего JAR вы просто «убиваете» свой экземпляр ClassLoader и создаете еще один (после замены JAR-файла).

Я не думаю, что это возможно для внутреннего класса Java Thread, потому что у вас нет контроля над System ClassLoader. Возможное решение состоит в том, чтобы иметь класс CustomThreadWeaver, который будет генерировать новый класс, расширяющий Thread с необходимыми переменными и использовать пользовательские DynamicWeavedThreadClassLoader для их загрузки.

удачи и показать нам свой монстра когда вам удастся ;-)

-2

То, что вы пытаетесь сделать, это не представляется возможным.

Поскольку вы уже знаете о ThreadLocal, вы уже знаете, что предлагает предлагаемое решение.

Кроме того, вы можете подклассифицировать поток и добавить свои собственные поля; однако только те потоки, которые вы явно создаете для этого класса, будут иметь эти поля, поэтому вам все равно придется «отступать» от использования локального потока.

Реальный вопрос: «почему?», Как в «Почему локальная нить недостаточна для ваших требований?»

+0

Это ваш реальный вопрос, а не мой :). Измерить разницу в производительности между наличием поля в потоке и использованием ThreadLocal, вот почему. Почему это невозможно? Там есть продукты, такие как JRebel, которые утверждают, что могут работать с возможностью «горячей» замены кода во время выполнения, так почему же нельзя отключить «горячую замену»? –

+0

Но что было неправильным в ответе? BTW, JRebel работает, изменяя классы во время загрузки, добавляя дополнительное поле к любому классу, который он должен иметь возможность изменять.Вы можете сделать это сами, изменив Thread.class в rt.jar, так как вам, похоже, не нравятся разумные предложения. – cpurdy

2

Это, кажется, вопрос использования правильного инструмента для работы. Аналогичный вопрос задан здесь: Another Stack Overflow Question и библиотека управления байтовым кодом Javaassist - это возможное решение.

Но без дальнейших подробностей о причинах, по которым это делается, кажется, что реальный ответ заключается в использовании правильного инструмента для работы. Например, с Groovy the ability to dynamically add methods to the language.

+0

Добавление нового метода не похоже на добавление нового поля, так что это не помогает. Если вы знаете, как добавить новое поле «на лету», используя Javaassist, скажите, пожалуйста. Мотивация - это интеллектуальное упражнение, мысль/идея на этом этапе. –

+0

Ах, я вижу. Я сам не использовал Javaassist, но я нашел этот учебник, который утверждает, что может добавить поле: http://www.csg.is.titech.ac.jp/~chiba/javassist/tutorial/tutorial2. html # add – droozen

1

Вы могли бы попробовать создать JVM агент, который делает использование java.lang.instrument API и более конкретно делают использование retransform method, что «облегчает инструментовку уже загруженных классов», а затем сделать использование Javassist (или ASM), как указано иметь дело с байт-код.

Более подробная информация о java.lang.instrument API

+0

К сожалению, документация утверждает, что это невозможно: «Переопределение может изменить тела методов, пул констант и атрибуты. Переопределение __must не добавлять, не удалять или переименовывать поля__ ...» см. [здесь] (http: // docs.oracle.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html#redefineClasses(java.lang.instrument.ClassDefinition ...)) –

0

Чтобы сделать то, что вам нужно, проще использовать подкласс Thread, запустить его, а затем внутри этого потока выполнить код из вашего примера (вместе с литом currentThread() в ваш подкласс).

+0

Обратите внимание, что этот подход также должен работать с динамически связанными подклассами созданный, например Javassist. – kutschkem

+0

Это не то, что я хочу, хотя ... Я хочу добавить поля «на лету». Не делать это на лету, как вы описываете. –

+0

Но для описания вы должны использовать динамические подклассы imho. Что делать, если у вас есть несколько потоков, в которых один использует A, а второй - B. Вы действительно хотите сгенерировать поле в том же классе Thread для обоих? Кроме того, проблема, которую я вижу, это то, что ThreadLocals являются членами экземпляра. Поэтому вы должны генерировать один объект для экземпляра A, а не одно поле для класса A (ok, я предполагаю, что пример был только там, чтобы продемонстрировать проблему) – kutschkem

4

Возможно с использованием инструментов и, возможно, библиотек, таких как javassist, для изменения кода на лету. (Однако, добавление и удаление полей, методов или конструкторов в настоящее время не представляется возможным)

//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)); 

Не забудьте добавить Can-Redefine-Classes: true в манифесте Java агента.

Реальный пример - оптимизация Java с помощью 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()));