微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

JVM入门

 

 JVM偏理论,主要靠背 面试题


JVM的位置

  JVM在操作系统之上,和其他的应用软件层级并列,在之上可以跑java程序

  .java => class文件 => 类加载器 Class Loader =>运行时数据区

 

 

JVM 总体系结构

 

 

 

类加载器 ClassLoader 

 

类加载器细分:

  1. bootStrap - 根  类加载器(jre/ lib/ rt.jar)

  2. ext - 扩展加载器 (jre/ lib/ ext/ ...)

  3. app - 应用程序(系统类) 加载器,最常用

 

 双亲委派机制

  选择类加载器的顺序:

    bootStrap->ext->app。先找boot,再找ext,最后找app也就是自己写的。都找不到就报错Class Not Found

  

  好处:

    防止用户乱定义类:比如自定义一个String类,这时会先从BootStrap找,所以不会轮到自定义的String类

 

 

沙箱安全机制

  Java安全模型的核心是 沙箱 sandBox

 

 

组成沙箱的基本组件:

  字节码校验器(bytecode verifier): 确保遵循java语言规范,帮助实现内存保护;核心类不参与校验;

  类加载器:双亲委派机制 + 将代码归入保护域

 

Native

例子:

private native void start0();    //java的范围达不到了,要调用C++的库了  //会进入本地方法栈,然后调用本地方法接口JNI(Java Native Interface)
进入本地方法栈,然后调用本地方法接口JNI(Java Native Interface)
JNI的作用:扩展Java的使用,一般用于驱动硬件

 

三种JVM

Sun公司:HotSpot、OpenJDK

其他:JRockit、J9VM

 

程序计数器PC

一个线程私有的,指向下一条指令的地址

PC寄存器:仅存一个指针,占用空间非常小

 

方法区(内含常量池)

方法区被所有线程共享,

方法区含有:静态变量static、常量final、类信息(构造方法、接口定义)、运行时的常量池

=>  static + final + Class + 常量池

(堆:实例变量)

 

  每一个方法内部结构 =》

栈帧

栈内存的生命周期 和线程同步

线程结束,栈内存就释放了;所以对于栈来说,不存在GC垃圾回收

栈的内容:8大基本类型 + 对象引用 + 实例的方法

 

栈溢出:StackOverflowError 是比较严重的错误

 

 

 


Heap,一个JVM只有一个堆内存,

堆内存的大小是可以调节的

  =》

 这两个用于调参

 

堆中的内容

  实例对象中的 方法、常量、变量

 

堆内存细分为3个区域:

  新生区 young 

  老年区 old

  永久区 perm

新生区-伊甸园 ,经过轻GC,到幸存区,再经过重GC,到老年区;

层层筛选后,等老年区也满了,就会OOM内存溢出

经验:99%的对象都是临时对象。

新生区

 

老年区

 

永久区(元空间)

==》元空间在逻辑上存在,但是在物理上是不存在的

元空间,常驻内存。关闭JVM会释放这个区域的内存。

用来存放JDK自身携带的Class对象,被所有的线程、对象共享;

Interface元数据,存储的是Java运行时的一些环境or类信息,这个区域不存在垃圾回收

 

永久区OOM:

  当一个启动类,加载了大量的第三方jar包

  Tomcat部署了太多的应用;

  大量动态生成的反射类

 

 

堆内存调优

public class Test {
    public static void main(String[] args) {
        long max = Runtime.getRuntime().maxMemory();
        System.out.println("max = " + max/1024/1024 + "MB");
        long total = Runtime.getRuntime().totalMemory();
        System.out.println("total = " + total/1024/1024 + "MB");
        //认max=1/4的总内存; total=1/64的总内存
    }
}

这台电脑的内存为32GB

 

 Configuration里面的VM Options设置为:

-xms1024m -Xmx1024m -XX:+PrintGCDetails

  jvm参数非常非常多,可以到网上去查到

 

 这里的总空间=新生区+老年区,不包括逻辑上的元空间

 

制造OOM:

-xms2m -Xmx2m -XX:+PrintGCDetails  ##设置堆的大小为2m
public class Test {
    public static void main(String[] args) {
        long max = Runtime.getRuntime().maxMemory();
        System.out.println("max = " + max/(double)1024/1024 + "MB");
        long total = Runtime.getRuntime().totalMemory();
        System.out.println("total = " + total/(double)1024/1024 + "MB");
        //认max=1/4的总内存; total=1/64的总内存

        String str = "123";
        while(true){
            str+="111";
        }
    }
}

 

解决OOM:

1)扩大堆内存,试下能不能解决

2)内存快照分析工具

 jprofiler 作用:

  • 分析Dump内存文件快速定位内存泄漏
  • 获得堆中的数据
  • 获得大的对象

 

插件

 设置dump导出错误信息:

-xms1m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
public class Test {
    public static void main(String[] args) {
        long max = Runtime.getRuntime().maxMemory();
        System.out.println("max = " + max/(double)1024/1024 + "MB");
        long total = Runtime.getRuntime().totalMemory();
        System.out.println("total = " + total/(double)1024/1024 + "MB");
        //认max=1/4的总内存; total=1/64的总内存

        byte[] array = new byte[1*1024*1024];  //1M

        ArrayList<Test>list = new ArrayList<Test>();
        int count = 0;

        try {
            while(true){
                list.add(new test());   //问题所在
                count++;
            }
        } catch (Error e) {
            System.out.println("count = " + count);
            e.printstacktrace();
        }

    }
}

执行上述程序,生成:Dumping heap to java_pid3044.hprof

1)双击打开,查看

 就知道哪里导致占用大量内存的问题了

 

2)点击 

 

 

 

GC:

垃圾回收器的区域是:堆+方法

 

GC  4个常用算法

1)引用计数法:

缺点:计数器本身损耗很多性能,效率不高

2)复制算法:

  ==》适用于复制率较低的 新生区(含:伊甸园区+2个幸存区)

两个幸存区轮流倒,from=>to,谁空谁是to

每次GC后,Eden区、from-幸存区 都是被倒空的

当1个对象经历15次GC,都还没有死,就会荣升 老年区

优点:没有内存的碎片,速度快

缺点: to区永远是空的,浪费了不少内存空间

3)标记清除法(两轮)

flag标记是否存活,一次扫描是否存活、一次扫描清除死的对象

缺点:两次扫描浪费时间;内存碎片

优点:不需要额外的空间(相对于复制算法)

4)标记清除压缩法(三轮)

防止内存碎片:增加一轮扫描,将对象移到一端;

 

@H_237_404@

 

GC算法总结:(下面均是越左越好)

时间消耗:复制算法 < 标记清除 < 标记压缩

内存碎片:复制算法 = 标记压缩 < 标记清除

内存利用率:标记压缩=标记清除 > 复制算法

 

GC:分代收集算法

年轻代:

  存活率低、复制率低

  => 复制算法

老年代:

  区域大,存活率高

  => 标记清除+标记压缩,混合实现(几次清除,一次压缩)

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐