深入理解java虚拟机2-笔记

版权声明:本文为博主原创文章,欢迎转载。 https://blog.csdn.net/guo_xl/article/details/91358266

虚拟机栈和本地方法栈溢出

一般本地方法栈很少溢出,虚拟机栈溢出比较常见

  • 代码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;


猜你喜欢

转载自blog.csdn.net/guo_xl/article/details/91358266
今日推荐