动态代理中的死循环导致的java.lang.StackOverflowError

目录

一、问题:循环打印直到栈溢出

二、代码:

1、接口及实现类

2、动态代理类

3、测试类

三、原因分析:

四、引申:$Proxy0其他可能出现死循环问题的代码及问题:


一、问题:循环打印直到栈溢出

...

拒绝中间商赚差价

拒绝中间商赚差价

拒绝中间商赚差价

Exception in thread "main" java.lang.StackOverflowError

at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)

at sun.nio.cs.UTF_8.access$200(UTF_8.java:57)

at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:636)

at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)

at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)

at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)

at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)

at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)

at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)

at java.io.PrintStream.write(PrintStream.java:526)

at java.io.PrintStream.print(PrintStream.java:669)

at java.io.PrintStream.println(PrintStream.java:806)

at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:13)

at com.sun.proxy.$Proxy0.toString(Unknown Source)

at java.lang.String.valueOf(String.java:2994)

at java.io.PrintStream.println(PrintStream.java:821)

at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:14)

at com.sun.proxy.$Proxy0.toString(Unknown Source)

at java.lang.String.valueOf(String.java:2994)

at java.io.PrintStream.println(PrintStream.java:821)

at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:14)

二、代码:

1、接口及实现类

public interface HouseRenting {

void rent(int money);

}

/**

* 被代理的类

* @author KentChiang

*

*/

public class HouseOwnerRenting implements HouseRenting {

@Override

public void rent(int money) {

System.out.println("房东以" + money + "元/月向外出租");

}

}

2、动态代理类

public class DynamicProxyHandler implements InvocationHandler{

private Object proxied;

public DynamicProxyHandler(Object proxied) {

this.proxied = proxied;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("拒绝中间商赚差价");

System.out.println(proxy);//导致死循环的代码

//return method.invoke(proxy, args);//这样写也会出问题

return method.invoke(proxied, args);

}

}

3、测试类

public class SimpleDynamicProxy {

public static void consumer(HouseRenting renting) {

renting.rent(1500);

}

public static void main(String[] args) {

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

HouseRenting renting = new HouseOwnerRenting();

HouseRenting proxy = (HouseRenting) Proxy.newProxyInstance(HouseRenting.class.getClassLoader(),

new Class[] { HouseRenting.class }, new DynamicProxyHandler(renting));

consumer(proxy);

}

}

三、原因分析:

动态代理类中加了System.out.println(proxy);才出现的问题,必然是这句话导致的,需要结合生成的com.sun.proxy.$Proxy0源码分析,通过设置即可保存生成的$Proxy0源码,保存在子工程目录下com\sun\proxy。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

部分Proxy代码如下。

public final class $Proxy0 extends Proxy

implements HouseRenting {

...

public final String toString()

throws

{

try

{

return (String)this.h.invoke(this, m2, null);

}

catch (Error|RuntimeException localError)

{

throw localError;

}

catch (Throwable localThrowable)

{

throw new UndeclaredThrowableException(localThrowable);

}

}

public final void rent(int paramInt)

throws

{

try

{

this.h.invoke(this, m3, new Object[] { Integer.valueOf(paramInt) });

return;

}

catch (Error|RuntimeException localError)

{

throw localError;

}

catch (Throwable localThrowable)

{

throw new UndeclaredThrowableException(localThrowable);

}

}...

static

{

try

{

m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });

m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

m3 = Class.forName("designPattern.proxy.service.HouseRenting").getMethod("rent", new Class[] { Integer.TYPE });

m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

return;

}

catch (NoSuchMethodException localNoSuchMethodException)

{

throw new NoSuchMethodError(localNoSuchMethodException.getMessage());

}

catch (ClassNotFoundException localClassNotFoundException)

{

throw new NoClassDefFoundError(localClassNotFoundException.getMessage());

}

}

可以看到toString中调用了 (String)this.h.invoke(this, m2, null);其中this就是$Proxy0,h是动态代理类,调用动态代理类重写的public Object invoke(Object proxy, Method method, Object[] args)方法,里面又调用$Proxy0的toString()方法,进入了死循环。

四、引申:$Proxy0其他可能出现死循环问题的代码及问题:

类似 this.h.invoke(this, mX, XX);这种形式的都可能因为我们编码不当出问题,把this作为参数想起来如果在动态代理类中把$Proxy0对象作为invoke方法的参数如return method.invoke(proxy, args)同样会引起死循环。

Caused by: java.lang.reflect.UndeclaredThrowableException

at com.sun.proxy.$Proxy0.rent(Unknown Source)

at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:15)

at com.sun.proxy.$Proxy0.rent(Unknown Source)

at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:15)