zoukankan      html  css  js  c++  java
  • 深入了解Java虚拟机(1)java内存区域与内存溢出异常

    java内存区域与内存溢出异常

    一、运行时数据区域

      

      1.程序计数器:线程私有,用于存储当前所执行的指令位置

      2.Java虚拟机栈:线程私有,描叙Java方法执行模型;执行方法时都会创建一个栈帧,存储局部变量,基本类型变量,引用等信息

      3.Java本地方法栈:线程私有,为虚拟机使用到的Native方法服务

      4.Java堆:线程共享,是垃圾收集器的主要工作地方;存储对象实例等

      5.方法区:线程共享;存储类信息,常量,静态变量等

        运行时常量:存放编译时生成的各种字面量和符号引用

      6.直接内存:机器的内存

    二、虚拟机对象

      1.对象的创建

    • 先检查常量池能否定位到此类的符号引用,并检查类是否已经加载初始化,否则要先执行加载过程;
    • 为对象分配内存:计算空间并从堆中划分一块连续或不连续的区域;使用的是cas+失败重试,避免线程安全问题(因为对象创建十分频繁,不知道当前内存有没有被分配出去)
    • 初始化内存空间:将分配的内存空间初始化0值
    • 设置对象基本信息:元数据、hash码、gc等
    • 执行java的init初始化:

      2.对象的内存布局

        对象头:存储对象的hash码、锁状态等 和 类型指针(对象所指向类的元数据)

        实例数据:对象真正存储的信息

        对齐填充:填充符合规则

      3.对象的访问定位

        对象的访问,通过java栈上的reference数据,它维护了一个指向对象的引用

        访问方式:句柄和直接访问

          

          句柄:堆中维护句柄池,reference指向句柄,句柄中包含了对象实例数据和类型数据的地址信息

            移动方便,直接修改句柄中的实例数据即可;开销大,多了一次指针定位

         

          直接:reference直接指向对象地址

            速度快

    三、实战OutofMemoryERROR

      1.java堆溢出

        参数:-Xms堆最小值;-Xmx堆最大值;-XX:+HeapDumpOnOutOfMemoryError出现溢出时内存快照分析

        堆中存放的是对象:可以创建大量对象来实现堆溢出:heap space

      2.栈溢出

        参数:-Xss设置栈值

        栈深度,可以通过无限递归增加栈深度、或创建大量线程实现

    //递归来StackOverFlower
    public class JavaVMStackSOF {
        private int stackLength = 1public void stackLeak(){
            stackLength++;
            stackLeak();
        }
        public static void main(String[] args)throws Throwable{
            JavaVMStackSOF oom = new JavaVMStackSOF();
            try {
                oom.stackLeak();
            } catch(Throwable e){
                System.out.println("stack length:" + oom.stackLength);
                throw e;
            }
        }
    }

      3.方法区和常量池溢出

        参数:-XX:PermSize方法区大小;-XX:MaxPermSize方法区最大大小

          在JDK1.6前,可以通过创建大量的String,虚拟机会复制对象放入常量池,从而溢出

          在1.7及以后,不可以这样,因为虚拟机只会在常量池中保存首次出现此对象时对象的引用

          方法区的溢出:方法区保存的是类的信息,通过产生大量的动态类来溢出,如spring其实也是通过动态代理产生的类

    public class JavaMethodAreaOOM{
        public static void main(String[]args){
            whiletrue){//创建大量的动态类,动态代理OOMObject
                Enhancer enhancer=new Enhancer();
                enhancer.setSuperclass(OOMObject.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor(){
                    public Object intercept(Object obj,Method method,Object[]args,MethodProxy proxy)throws Throwable{
                        return proxy.invokeSuper(obj,args);
                    }}
                );
                enhancer.create();
            }}
            static class OOMObject{
        }
    }

      String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用

      JDK6及以前:方法区(永久代)是单独的,常量池在方法区内

      JDK7:去永久代

    public class RuntimeConstantPoolOOM{
        public static void main(String[]args){
            String str1=new StringBuilder("计算机").append("软件").toString();
            System.out.println(str1.intern()==str1);
            String str2=new StringBuilder("ja").append("va").toString();
            System.out.println(str2.intern()==str2);
        }
    } 

      这段代码在JDK 1.6中运行,会得到两个false,而在JDK 1.7中运行,会得到一个true和一个false。

      产生差异的原因是:在JDK 1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用,而由StringBuilder创建的字符串实例在Java堆上,所以必然不是同一个引用,将返回false。

      而JDK 1.7:intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。

      对str2比较返回false是因为“java”这个字符串在执行StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用了,不符合“首次出现”的原则,而“计算机软件”这个字符串则是首次出现的,因此返回true

      注意:1.7及以后保存的是首次出现的引用;理解上面的分析

       4.本机直接内存

        参数:-XX:MaxDirectMemorySize直接内存大小;默认==最大堆内存

  • 相关阅读:
    [Java][Android][Process] 分享 Process 运行命令行封装类型
    UVA 11992
    2014扬声器的信息中国建筑师大会
    POJ 1745 Divisibility (线性dp)
    ListView 实现多选/无线电
    UVa 11587
    zoj 2156
    [TroubleShooting] The server network address can not be reached or does not exist
    oracle,如何查看视图结构,获得视图中的字段名称、字段类型、字段长度等。
    实现文件上传,以及表单提交成功的回调函数
  • 原文地址:https://www.cnblogs.com/zhangxinly/p/6954118.html
Copyright © 2011-2022 走看看