我有一个任务得到" StackOverflowError"在java中没有使用-Xss和递归。我真的没有想法......只有一些废话,比如在运行时生成巨大的java类,编译它并调用...
答案 0 :(得分:3)
编辑:
答案是不正确的,因为它是一种递归。它被称为间接递归https://en.wikipedia.org/wiki/Recursion_(computer_science)#Indirect_recursion。
我认为没有递归的最简单方法是:
import java.util.LinkedList;
import java.util.List;
interface Handler {
void handle(Chain chain);
}
interface Chain {
void process();
}
class FirstHandler implements Handler {
@Override
public void handle(Chain chain) {
System.out.println("first handler");
chain.process();
}
}
class SecondHandler implements Handler {
@Override
public void handle(Chain chain) {
System.out.println("second handler");
chain.process();
}
}
class Runner implements Chain {
private List<Handler> handlers;
private int size = 5000; // change this parameter to avoid stackoverflowerror
private int n = 0;
public static void main(String[] args) {
Runner runner = new Runner();
runner.setHandlers();
runner.process();
}
private void setHandlers() {
handlers = new LinkedList<>();
int i = 0;
while (i < size) {
// there can be different implementations of handler interface
handlers.add(new FirstHandler());
handlers.add(new SecondHandler());
i += 2;
}
}
public void process() {
if (n < size) {
Handler handler = handlers.get(n++);
handler.handle(this);
}
}
}
乍一看这个例子看起来有点疯狂,但它并不像看起来那么不现实。
这种方法的主要思想是责任链模式。您可以通过实施责任链模式在现实生活中重现此异常。例如,在做了一些逻辑调用链中的下一个对象之后,你有一些对象和每个对象,并将他的工作结果传递给下一个。
您可以在java过滤器(javax.servlet.Filter)中看到这一点。 我不知道使用这个类的详细机制,但它使用doFilter方法调用链中的下一个过滤器,并且在所有过滤器/ servlet处理请求之后,它继续在doFilter下面的相同方法中工作。
换句话说,它在servlet之前和向客户端发送响应之前拦截请求/响应。这是一段危险的代码,因为所有被调用的方法都位于同一个线程的同一堆栈中。因此,如果链太大或者您在深层调用doFilter方法也会提供相同的情况,它可能会启动stackoverflow异常。也许,在调试期间,您可能会看到一连串的调用 在一个线程中,它可能是stackoverflower的原因。
此外,您可以从下面的链接中获取责任链模式示例,并添加元素集合而不是几个,您也将获得stackoverflower错误。
与模式的链接:
https://www.journaldev.com/1617/chain-of-responsibility-design-pattern-in-java https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern
我希望它对你有所帮助。
答案 1 :(得分:2)
Java在栈上存储原始类型。在本地作用域中创建的对象在堆上分配,并在堆栈上引用它们。
通过在方法范围中分配太多基本类型,可以在不递归的情况下溢出堆栈。使用正常的堆栈大小设置,您必须分配过多的变量才能溢出。
答案 2 :(得分:2)
由于这个问题非常有趣,我试图简化 hide 的答案:
public class Stackoverflow {
static class Handler {
void handle(Chain chain){
chain.process();
System.out.println("yeah");
}
}
static class Chain {
private List<Handler> handlers = new ArrayList<>();
private int n = 0;
private void setHandlers(int count) {
int i = 0;
while (i++ < count) {
handlers.add(new Handler());
}
}
public void process() {
if (n < handlers.size()) {
Handler handler = handlers.get(n++);
handler.handle(this);
}
}
}
public static void main(String[] args) {
Chain chain = new Chain();
chain.setHandlers(10000);
chain.process();
}
}
重要的是要注意,如果发生stackoverflow,将永远不会输出字符串“yeah”。
答案 3 :(得分:1)
这是Eric J.想法的实现,该想法使用javassist库生成过多的局部变量:
class SoeNonRecursive {
static final String generatedMethodName = "holderForVariablesMethod";
@SneakyThrows
Class<?> createClassWithLotsOfLocalVars(String generatedClassName, final int numberOfLocalVarsToGenerate) {
ClassPool pool = ClassPool.getDefault();
CtClass generatedClass = pool.makeClass(generatedClassName);
CtMethod generatedMethod = CtNewMethod.make(getMethodBody(numberOfLocalVarsToGenerate), generatedClass);
generatedClass.addMethod(generatedMethod);
return generatedClass.toClass();
}
private String getMethodBody(final int numberOfLocalVarsToGenerate) {
StringBuilder methodBody = new StringBuilder("public static long ")
.append(generatedMethodName).append("() {")
.append(System.lineSeparator());
StringBuilder antiDeadCodeEliminationString = new StringBuilder("long result = i0");
long i = 0;
while (i < numberOfLocalVarsToGenerate) {
methodBody.append(" long i").append(i)
.append(" = ").append(i).append(";")
.append(System.lineSeparator());
antiDeadCodeEliminationString.append("+").append("i").append(i);
i++;
}
antiDeadCodeEliminationString.append(";");
methodBody.append(" ").append(antiDeadCodeEliminationString)
.append(System.lineSeparator())
.append(" return result;")
.append(System.lineSeparator())
.append("}");
return methodBody.toString();
}
}
和测试:
class SoeNonRecursiveTest {
private final SoeNonRecursive soeNonRecursive = new SoeNonRecursive();
//Should be different for every case, or once generated class become
//"frozen" for javassist: http://www.javassist.org/tutorial/tutorial.html#read
private String generatedClassName;
@Test
void stackOverflowWithoutRecursion() {
generatedClassName = "Soe1";
final int numberOfLocalVarsToGenerate = 6000;
assertThrows(StackOverflowError.class, () -> soeNonRecursive
.createClassWithLotsOfLocalVars(generatedClassName, numberOfLocalVarsToGenerate));
}
@SneakyThrows
@Test
void methodGeneratedCorrectly() {
generatedClassName = "Soe2";
final int numberOfLocalVarsToGenerate = 6;
Class<?> generated = soeNonRecursive.createClassWithLotsOfLocalVars(generatedClassName, numberOfLocalVarsToGenerate);
//Arithmetic progression
long expected = Math.round((numberOfLocalVarsToGenerate - 1.0)/2 * numberOfLocalVarsToGenerate);
long actual = (long) generated.getDeclaredMethod(generatedMethodName).invoke(generated);
assertEquals(expected, actual);
}
}
答案 4 :(得分:0)
我们当然可以做到:)。根本没有递归!
public static void main(String[] args) {
throw new StackOverflowError();
}
答案 5 :(得分:-2)
看下面这个答案,不确定这是否适用于Java,但听起来你可以声明一个指针数组?可以在不需要发电机的情况下实现Eric J的想法。
int* x[LARGENUMBER]; // The addresses are held on the stack
int i; // On the stack
for(i = 0; i < LARGENUMBER; ++i)
x[i] = malloc(sizeof(int)*10); // Allocates memory on the heap