HotswapProjects / HotswapAgent

Java unlimited redefinition of classes at runtime.
GNU General Public License v2.0
2.33k stars 491 forks source link

Infinite stream of hotswapping events #589

Closed Artur- closed 1 week ago

Artur- commented 1 week ago

When using the Vaadin plugin in 2.0.0, I every now and then see an infinite stream of hotswapping events for the same class being sent to Hotswapper.onHotswap. Today it looks like

Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]
Hotswapping[com.github.javaparser.resolution.declarations.AssociableToAST]

and it continues forever. Would this be a problem in the Vaadin plugin, HotswapAgent or JBR?

The stack trace in IntelliJ does not really tell that much image

Artur- commented 1 week ago

With trace logging enabled for HA, I see

HOTSWAP AGENT: 10:37:05.105 DEBUG (org.hotswap.agent.command.impl.SchedulerImpl) - Executing Command{class='com.vaadin.flow.hotswap.Hotswapper', methodName='onHotswap'}
HOTSWAP AGENT: 10:37:05.105 TRACE (org.hotswap.agent.command.impl.CommandExecutor) - Executing command Command{class='com.vaadin.flow.hotswap.Hotswapper', methodName='onHotswap'}
HOTSWAP AGENT: 10:37:05.106 TRACE (org.hotswap.agent.command.ReflectionCommand) - Executing command: requestedClassLoader=jdk.internal.loader.ClassLoaders$AppClassLoader@27ddd392, resolvedClassLoader=jdk.internal.loader.ClassLoaders$AppClassLoader@27ddd392, class=class com.vaadin.flow.hotswap.Hotswapper, method=onHotswap, params=[[Ljava.lang.String;@669856bd, false]
Hotswapping[com.vaadin.flow.component.dependency.StyleSheet]
HOTSWAP AGENT: 10:37:05.208 DEBUG (org.hotswap.agent.command.impl.SchedulerImpl) - Executing Command{class='com.vaadin.flow.hotswap.Hotswapper', methodName='onHotswap'}
HOTSWAP AGENT: 10:37:05.208 TRACE (org.hotswap.agent.command.impl.CommandExecutor) - Executing command Command{class='com.vaadin.flow.hotswap.Hotswapper', methodName='onHotswap'}
HOTSWAP AGENT: 10:37:05.208 TRACE (org.hotswap.agent.command.ReflectionCommand) - Executing command: requestedClassLoader=jdk.internal.loader.ClassLoaders$AppClassLoader@27ddd392, resolvedClassLoader=jdk.internal.loader.ClassLoaders$AppClassLoader@27ddd392, class=class com.vaadin.flow.hotswap.Hotswapper, method=onHotswap, params=[[Ljava.lang.String;@669856bd, false]
Hotswapping[com.vaadin.flow.component.dependency.StyleSheet]
HOTSWAP AGENT: 10:37:05.313 DEBUG (org.hotswap.agent.command.impl.SchedulerImpl) - Executing Command{class='com.vaadin.flow.hotswap.Hotswapper', methodName='onHotswap'}
HOTSWAP AGENT: 10:37:05.313 TRACE (org.hotswap.agent.command.impl.CommandExecutor) - Executing command Command{class='com.vaadin.flow.hotswap.Hotswapper', methodName='onHotswap'}
HOTSWAP AGENT: 10:37:05.313 TRACE (org.hotswap.agent.command.ReflectionCommand) - Executing command: requestedClassLoader=jdk.internal.loader.ClassLoaders$AppClassLoader@27ddd392, resolvedClassLoader=jdk.internal.loader.ClassLoaders$AppClassLoader@27ddd392, class=class com.vaadin.flow.hotswap.Hotswapper, method=onHotswap, params=[[Ljava.lang.String;@669856bd, false]
Hotswapping[com.vaadin.flow.component.dependency.StyleSheet]
skybber commented 1 week ago

Could you check please from which method SchedulerImpl.scheduleCommand() is called?

Artur- commented 1 week ago

It originates here https://github.com/HotswapProjects/HotswapAgent/blob/24007fd03a29309f54a05bdc3921ac142199ad76/plugin/hotswap-agent-vaadin-plugin/src/main/java/org/hotswap/agent/plugin/vaadin/VaadinPlugin.java#L257-L260

Artur- commented 1 week ago

But the Reloading class {} because of {}" is not constantly logged, so it would seem that scheduleCommand is only called once

Artur- commented 1 week ago

There are a lot of commands "running"

Screenshot 2024-09-18 at 14 03 34

and in SchedulerImpl.finished() some gets removed from runningCommands but then when onHotswap is called again, scheduledCommands contain the same commands (in this case it is two, apparently it can also be one) and the ones in runningCommands also seem to remain

skybber commented 1 week ago
@OnClassLoadEvent(classNameRegexp = ".*", events = {LoadEvent.DEFINE, LoadEvent.REDEFINE})
Artur- commented 1 week ago

The problem is that it.remove() does not remove the command. Also when debugging I can see that after

            for (Iterator<Map.Entry<Command, DuplicateScheduleConfig>> it = scheduledCommands.entrySet().iterator(); it.hasNext(); ) {
                Map.Entry<Command, DuplicateScheduleConfig> entry = it.next();

then scheduledCommands.containsKey(entry.getKey()) is false, or in other words scheduledCommands.containsKey(scheduledCommands.entrySet().iterator().next().getKey()) is false. This seems like the hash code of ReflectionCommand could change after adding the command to the map or something similar

Artur- commented 1 week ago

It can, because ReflectionCommand.getTargetClassLoader() modifies the targetClassLoader field when it is null, and after that, the hash code no longer matches

skybber commented 1 week ago

It looks like a bug. I suggest making all attributes in the ReflectionCommand class final and initializing them in the constructor.

skybber commented 1 week ago

A new fixed version is available at:

https://github.com/HotswapProjects/HotswapAgent/releases/tag/2.0.1-SNAPSHOT

Could you please check if it resolves your issue?

Artur- commented 1 week ago

Thanks! I think it resolves the issue but as it is a timing issue, I cannot say for sure

Artur- commented 1 week ago

Haven't seen it again so far

skybber commented 1 week ago

Problem is fixed in version 2.0.1 , is is released on maven https://mvnrepository.com/artifact/org.hotswapagent