虚拟机栈和本地方法栈溢出
一般本地方法栈很少溢出,虚拟机栈溢出比较常见
- 代码1
//VM args :-Xss128k 指定帧栈的容量
public void hello() {
hello();
}
public static void main(String[] args) {
StackOverFlow stackOverFlow = new StackOverFlow();
//线程栈里的VMstack是有限的,也就是容纳的栈帧数是有限的
//栈帧数不断的增加会最后导致stack over flow,叫栈溢出
stackOverFlow.hello();
}
报错
Exception in thread "main" java.lang.StackOverflowError
at rechard.learn.jvm.StackOverFlow.hello(StackOverFlow.java:8)
at rechard.learn.jvm.StackOverFlow.hello(StackOverFlow.java:8)
- 代码2
public static void main(String[]args){
while(true){
//jvm里的内存是有限的,
//线程创建出来,线程栈是需要一定的内存的
//一直创建线程,并且线程一直在运行,也就是GC不会回收这些线程,
//迟早会出现内存溢出,出现oom
//解决方法:
//1.控制线程数,通过线程池控制。 Excutors.newxxxx
//2.-xss:减小线程栈的内存分配以达到能创建更多的线程数
new Thread(new Runnable(){
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
报错
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:717)
at rechard.learn.jvm.StackoutOfMemory.main(StackoutOfMemory.java:25)
堆内存不足
//vm args:-Xmx20M -Xms20M -verbose:gc
//堆里不断的创建对象,最后会造成堆里的内存空间不足,而导致oom
public static void main(String []args){
List cases = new ArrayList();
while(true){
cases.add(new Object());
}
}
报错如下
[GC (Allocation Failure) 5572K->2988K(19968K), 0.0081114 secs]
[GC (Allocation Failure) 8620K->7087K(19968K), 0.0150993 secs]
[GC (Allocation Failure) 12719K->12733K(19968K), 0.0168034 secs]
[Full GC (Ergonomics) 12733K->10732K(19968K), 0.2536876 secs]
[Full GC (Ergonomics) 16364K->14254K(19968K), 0.1547094 secs]
[Full GC (Ergonomics) 16696K->16599K(19968K), 0.2127199 secs]
[Full GC (Allocation Failure) Exception in thread "main" 16599K->16580K(19968K), 0.1892626 secs]
java.lang.OutOfMemoryError: Java heap space
方法区溢出
运行时常量池是方法区里的一部分,运行时常量池在1.8里已经放在堆里了
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
int i = 0;
while(true){
list.add(String.valueOf(i++).intern());
//System.out.println(i);
}
}
报错
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.lang.Integer.toString(Integer.java:401)
at java.lang.String.valueOf(String.java:3099)
at rechard.learn.jvm.StringInternOOM.main(StringInternOOM.java:12)
加上-verbose:gc -Xmx20M
后
[Full GC (Ergonomics) 19455K->19455K(19968K), 0.1024099 secs]
[Full GC (Allocation Failure) 19455K->19455K(19968K), 0.1182977 secs]
[Full GC (Ergonomics) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
可见 堆最后为19455K->19455K(19968K) ,表明了常量池在堆里
奇怪的是放开上面的代码中的System.out.println(i);
,报错却成了
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.toString(Integer.java:401)
at java.lang.String.valueOf(String.java:3099)
at java.io.PrintStream.print(PrintStream.java:597)
at java.io.PrintStream.println(PrintStream.java:736)
at rechard.learn.jvm.StringInternOOM.main(StringInternOOM.java:13)
超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。可以用
-XX:-UseGCOverheadLimit 禁用这个检查 就会变成
[Full GC (Ergonomics) Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
String.intern
When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
官方解释:如果常量池里已经含有了这个string对象,那么这个对象直接返回,如果没有的话,加入这个对象到常量池里然后返回
public static void main(String[] args) {
String b=new String ("A").intern();//创建后返回
String a="A";
String c=new String("A");
System.out.println(a==b);
System.out.println(a==c);
String d=new String ("B");
String d1=d.intern();
System.out.println(d==d1);
System.out.println(d1=="B");
}
运行结果
true
false
false
true
通过javap -v StringDemo.class分析
constant pool:
#1 = Methodref #10.#34 // java/lang/Object."<init>":()V
#2 = Class #35 // java/lang/String
#3 = String #36 // A
。。。
Code:
stack=3, locals=6, args_size=1
0: new #2 // class java/lang/String new 一个引用类型并压入栈顶
3: dup //对栈顶复制一份并压入栈顶
4: ldc #3 // String A 将int ,float或String 类型从本地变量表里的压栈,这里是#3,即对应constant pool里的#3,即将A 入栈
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V // 调用<init>
9: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String; //调用intern
12: astore_1 // 9里执行完其实是将常量池里的"A"的引用压栈,astore_1 是将栈顶的引用存入到第2个本地变量中,本地变量表是虚拟机栈的一部分,对应上面的locals=6是说本地变量表 有6个,对应下面的 LocalVariableTable 里的13 100 1 b Ljava/lang/String; 即将"A" 付给了b
13: ldc #3 // String A //同上面4的注释
15: astore_2 // 对应下面的LocalVariableTable里的第3行,即将"A"付给了a
。。。
LocalVariableTable:
Start Length Slot Name Signature
0 113 0 args [Ljava/lang/String;
13 100 1 b Ljava/lang/String;
16 97 2 a Ljava/lang/String;