BlockHound icon indicating copy to clipboard operation
BlockHound copied to clipboard

OpenJDK 13 requires -XX:+AllowRedefinitionToAddDeleteMethods flag

Open juergenzimmermann opened this issue 5 years ago • 20 comments

OpenJDK 12.0.1 with even Kotlin 1.3.40-eap-67 works fine. However, using OpenJDK 13 build 23 I get the following exception:

Exception in thread "main" java.lang.RuntimeException: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
        at reactor.blockhound.BlockHound$Builder.install(BlockHound.java:264)
        at reactor.blockhound.BlockHound.install(BlockHound.java:64)
        at de.hska.kunde.ApplicationKt.main(Application.kt:51)
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
        at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
        at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
        at reactor.blockhound.BlockHound$Builder.instrument(BlockHound.java:273)
        at reactor.blockhound.BlockHound$Builder.install(BlockHound.java:261)
        ... 2 more

juergenzimmermann avatar Jun 02 '19 11:06 juergenzimmermann

Hi @juergenzimmermann,

I'm afraid that's due to https://bugs.openjdk.java.net/browse/JDK-8192936 and similar to #21 In JDK 13+, they adjusted the spec & impl and no longer allow redefining native methods :(

We will need to find an alternative way for 13+ and J9, meanwhile, they seem to plan adding a flag to keep the behaviour for a few versions: https://bugs.openjdk.java.net/browse/JDK-8192936?focusedCommentId=14254266&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-14254266

bsideup avatar Jun 02 '19 11:06 bsideup

@bsideup When I use -XX:+AllowRedefinitionToAddDeleteMethods the stacktrace goes away

juergenzimmermann avatar Jun 02 '19 16:06 juergenzimmermann

@juergenzimmermann thanks a lot for trying it! I will keep this issue open as a reminder to document this flag 👍

bsideup avatar Jun 02 '19 18:06 bsideup

Heaving quite different behavior on Oracle JDK 13 in Windows: it does not throw an error about blocking call until I add this flag

yburkouski avatar Nov 09 '19 14:11 yburkouski

Still reproduced with JDK 14.0.1 and BlockHound 1.0.3

sergey-morenets avatar May 10 '20 17:05 sergey-morenets

Using Blockhound 1.0.4 and JDK 14.0.2 and specify the following in surefire plugin worked:

<plugins>
 ...
         <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
             <version>2.22.2</version>
             <configuration>
                 <argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
             </configuration>
         </plugin>
 ...
 </plugins>

jaschenk avatar Jul 30 '20 17:07 jaschenk

any way this can be added to gradle?

abdullahumer avatar Nov 29 '20 19:11 abdullahumer

@abdullahumer sure:

    tasks.withType(Test).all {
        if (JavaVersion.current().isCompatibleWith​(JavaVersion.VERSION_13)) {
            jvmArgs += [
                    "-XX:+AllowRedefinitionToAddDeleteMethods"
            ]
        }
    }

bsideup avatar Nov 30 '20 12:11 bsideup

Thanks! @bsideup

abdullahumer avatar Dec 01 '20 09:12 abdullahumer

Still reproduced with JDK 15.0.2 and BlockHound 1.0.6.RELEASE. "-XX:+AllowRedefinitionToAddDeleteMethods" option still shows deprecation warning. Any updates?

roimenashe avatar Aug 25 '21 13:08 roimenashe

Using Blockhound 1.0.4 and JDK 14.0.2 and specify the following in surefire plugin worked:

<plugins>
 ...
         <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
             <version>2.22.2</version>
             <configuration>
                 <argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
             </configuration>
         </plugin>
 ...
 </plugins>

In my case Using Blockhound 1.0.6 and JDK 15.0.6, worked

CodeGTDLearn avatar Feb 17 '22 18:02 CodeGTDLearn

Using blockhound 1.0.6 and JDK 16.0.2 I am still getting this error and has to use the flag...

KrzysztofKwiatkowskiK avatar Feb 24 '22 13:02 KrzysztofKwiatkowskiK

Hi. As far as I understand, according to https://bugs.openjdk.org/browse/JDK-8221528.

"This option (AllowRedefinitionToAddOrDeleteMethods) is deprecated right away. The plan is to keep it for a couple of releases to allow customers (tool vendors) to remove dependency on old behavior from their tools."

Do you plan BlockHound work according to the new JVM TI behavior?

ajax-semenov-y avatar Sep 17 '22 13:09 ajax-semenov-y

I guess that if this issue is not solved, when the option AllowRedefinitionToAddOrDeleteMethods is removed from JVM, then Blockhound is completely unusable, right?

juliojgd avatar Dec 19 '22 10:12 juliojgd

Now in 2023, almost four years after the original issue, would it be possible to have a fix without the -XX workaround please?

patpatpat123 avatar May 15 '23 06:05 patpatpat123

I will take a look, but I don't promise anything for the moment, because looking at the history of this issue, without the work around flag, it seems to be a very very big deal to redefine / retransform classes (add/delete private static and private final instance methods in the new class versions). so, I'm afraid that for the moment, we need to continue using -XX:+AllowRedefinitionToAddDeleteMethods for JDK13+ Is there any issues if you continue using this flag for the moment ?

thanks.

pderop avatar May 15 '23 07:05 pderop

i am using Java 17 and Spring 3.1.3, even after including the -XX:+AllowRedefinitionToAddDeleteMethods flag, i am still facing the error. Is the flag removed from JVM or has the fix stopped working?

divyanshsaini1805 avatar Oct 17 '23 07:10 divyanshsaini1805

Hi @divyanshsaini1805 ,

please open a separate issue with a reproducer project; From my side, I did the following which worked for me using Spring 3.1.3, and java 17:

build.gradle:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.3'
    id 'io.spring.dependency-management' version '1.1.3'
}

apply plugin: 'application'

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'io.projectreactor.netty:reactor-netty-core:1.0.37'
    implementation 'io.projectreactor.netty:reactor-netty-http:1.0.37'
    implementation('io.projectreactor.tools:blockhound:1.0.8.RELEASE')
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

task(echoServer, dependsOn: 'classes', type: JavaExec) {
    main = 'com.example.EchoServer'
    classpath = sourceSets.main.runtimeClasspath
    args ''
    systemProperty "io.netty.leakDetectionLevel", "DISABLED"
    debugOptions {
        enabled = false
        port = 5005
        server = true
        suspend = true
    }
}

DemoApplication.java:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

Demo.java:

package com.example.demo;

import org.springframework.stereotype.Component;
import reactor.blockhound.BlockHound;
import reactor.netty.tcp.TcpServer;

import javax.annotation.PostConstruct;

@Component
public class Demo {
    static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));

    @PostConstruct
    public void init() {
        System.out.println("Jdk version used: " + System.getProperty("java.version"));
        BlockHound.install();

        TcpServer server =
                TcpServer.create()
                        .port(PORT)
                        .handle((in, out) -> {
                            try {
                                Thread.sleep(100);
                            } catch (Throwable err) {
                                err.printStackTrace();
                            }
                            return out.send(in.receive().retain());
                        });

        server.bindNow()
                .onDispose()
                .block();
    }
}

then, from one console using java17:

./gradlew build
java -XX:+AllowRedefinitionToAddDeleteMethods -jar build/libs/demo-0.0.1-SNAPSHOT.jar

from another console:

curl -v http://localhost:8080/foo

this will display the following from the first console:

reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep
	at java.base/java.lang.Thread.sleep(Thread.java)
	at com.example.demo.Demo.lambda$init$0(Demo.java:34)
	at reactor.netty.tcp.TcpServer$OnConnectionHandle.accept(TcpServer.java:277)
	at reactor.netty.tcp.TcpServer$OnConnectionHandle.accept(TcpServer.java:264)
	at reactor.netty.transport.ServerTransportConfig$ServerTransportDoOnConnection.onStateChange(ServerTransportConfig.java:253)
	at reactor.netty.transport.ServerTransport$ChildObserver.onStateChange(ServerTransport.java:481)
	at reactor.netty.channel.ChannelOperationsHandler.channelActive(ChannelOperationsHandler.java:62)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:262)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:238)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:231)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(DefaultChannelPipeline.java:1398)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:258)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:238)
	at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:895)
	at io.netty.channel.AbstractChannel$AbstractUnsafe.register0(AbstractChannel.java:522)
	at io.netty.channel.AbstractChannel$AbstractUnsafe.access$200(AbstractChannel.java:429)
	at io.netty.channel.AbstractChannel$AbstractUnsafe$1.run(AbstractChannel.java:486)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)

Thanks.

pderop avatar Oct 19 '23 15:10 pderop

I have tried on windows with corretto-17 and temurin-17. When I add the vm option to the Surefire plugin config in pom.xml, my unit tests work with BlockHound. But when I add the vm option to my IntelliJ run configuration or in the vm options file I still see this error when I start up my springboot app:

OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.IllegalStateException: The instrumentation have failed.
It looks like you're running on JDK 13+.
You need to add '-XX:+AllowRedefinitionToAddDeleteMethods' JVM flag.
public static void main(String[] args) {
	System.out.println(args[0] + " ");
	SpringApplication.run(BillGenerationServiceApplication.class, args);
}

I see this: -XX:+AllowRedefinitionToAddDeleteMethods at the top of the console.

Any ideas? Thanks.

apferrarone avatar Jan 12 '24 07:01 apferrarone

Hi @divyanshsaini1805 ,

please open a separate issue with a reproducer project; From my side, I did the following which worked for me using Spring 3.1.3, and java 17:

build.gradle:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.3'
    id 'io.spring.dependency-management' version '1.1.3'
}

apply plugin: 'application'

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'io.projectreactor.netty:reactor-netty-core:1.0.37'
    implementation 'io.projectreactor.netty:reactor-netty-http:1.0.37'
    implementation('io.projectreactor.tools:blockhound:1.0.8.RELEASE')
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

task(echoServer, dependsOn: 'classes', type: JavaExec) {
    main = 'com.example.EchoServer'
    classpath = sourceSets.main.runtimeClasspath
    args ''
    systemProperty "io.netty.leakDetectionLevel", "DISABLED"
    debugOptions {
        enabled = false
        port = 5005
        server = true
        suspend = true
    }
}

DemoApplication.java:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

Demo.java:

package com.example.demo;

import org.springframework.stereotype.Component;
import reactor.blockhound.BlockHound;
import reactor.netty.tcp.TcpServer;

import javax.annotation.PostConstruct;

@Component
public class Demo {
    static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));

    @PostConstruct
    public void init() {
        System.out.println("Jdk version used: " + System.getProperty("java.version"));
        BlockHound.install();

        TcpServer server =
                TcpServer.create()
                        .port(PORT)
                        .handle((in, out) -> {
                            try {
                                Thread.sleep(100);
                            } catch (Throwable err) {
                                err.printStackTrace();
                            }
                            return out.send(in.receive().retain());
                        });

        server.bindNow()
                .onDispose()
                .block();
    }
}

then, from one console using java17:

./gradlew build
java -XX:+AllowRedefinitionToAddDeleteMethods -jar build/libs/demo-0.0.1-SNAPSHOT.jar

from another console:

curl -v http://localhost:8080/foo

this will display the following from the first console:

reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep
	at java.base/java.lang.Thread.sleep(Thread.java)
	at com.example.demo.Demo.lambda$init$0(Demo.java:34)
	at reactor.netty.tcp.TcpServer$OnConnectionHandle.accept(TcpServer.java:277)
	at reactor.netty.tcp.TcpServer$OnConnectionHandle.accept(TcpServer.java:264)
	at reactor.netty.transport.ServerTransportConfig$ServerTransportDoOnConnection.onStateChange(ServerTransportConfig.java:253)
	at reactor.netty.transport.ServerTransport$ChildObserver.onStateChange(ServerTransport.java:481)
	at reactor.netty.channel.ChannelOperationsHandler.channelActive(ChannelOperationsHandler.java:62)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:262)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:238)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:231)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(DefaultChannelPipeline.java:1398)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:258)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:238)
	at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:895)
	at io.netty.channel.AbstractChannel$AbstractUnsafe.register0(AbstractChannel.java:522)
	at io.netty.channel.AbstractChannel$AbstractUnsafe.access$200(AbstractChannel.java:429)
	at io.netty.channel.AbstractChannel$AbstractUnsafe$1.run(AbstractChannel.java:486)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)

Thanks.

hi, i know it's been a super long time, but since this thread is a lot active, i would like to add my part, for Spring 3.1.3, and java 17, the -XX:+AllowRedefinitionToAddDeleteMethods flag works good indeed if used with the following command: java -XX:+AllowRedefinitionToAddDeleteMethods -jar target/Required_jar_file.jar,

divyanshsaini1805 avatar Jan 16 '24 10:01 divyanshsaini1805