面试专辑

深入理解Java虚拟机

2017-01-14  本文已影响232人  yulongsun

[学习笔记] 深入理解Java虚拟机

标签(空格分隔): java


第二部分.自动内存管理机制

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

<center> java内存区域java内存区域

</center>

2.1.方法区(永久代)

2.2.堆(内存中最大的一块)

堆内存划分堆内存划分

2.3.虚拟机栈

注意
- 当线程栈深度超过 虚拟机栈所允许的最大深度的时候,就会抛StackOverflowError.
- 如果虚拟机栈扩展时,无法获取到足够的内存,就会抛OOM.

2.4.本地方法栈

2.5.程序寄存器

3.典型的垃圾回收算法

http://icyfenix.iteye.com/blog/715301

3.2 对象已死吗?

java堆中几乎存放了所有的对象,垃圾回收器在回收之前需要确定哪些对象还”活着“,哪些对象已经”死了“

3.2.1 引用计数法(Java没有使用)

优点:实现简单,效率高
缺点: 无法处理循环引用
原理:给对象添加一个引用,每当有一个地方引用这个对象的时候,计算器就+1,当引用失效的时候,引用就-1,当引用计算器=0的时候表示该对象不被引用。
    需要注意的是:引用计数器很难解决对象之间循环引用,所以主流java虚拟机没有使用引用计数器来管理内存。

3.2.2 可达性分析法

可达性分析法可达性分析法
- 强引用:GC永远不会回收引用对象。
- 软引用SoftReference:在内存溢出之前,会进行第二次回收,如果回收之后还没有足够的内存,才会抛出OOM。
- 弱引用WeakReference:引用对象只能生存到下一次GC之前。
- 虚引用(幽灵引用、幻影引用)PhantomReference:唯一目的就是GC在回收时收到一个系统通知。

3.2.3 finalize

finalize只会被执行一次。

一个对象真正被回收需要经历两次标记过程:
如果一个对象在可达性分析后没有与GC ROOT相连接的引用链,那就被被第一次标记并且进行一次筛选:
如果finalize没有被重写或调用过,虚拟机会让认为“没必要执行”。
否则,会执行finalize方法

finalize:
如果调用finalize方法,那么这个对象将会被放到一个队列里面[虚拟机建立并执行Finalizer线程执行调用],等待被回收。

finalize是对象逃脱死亡命运的最后机会。只要与引用链上的任何一个对象建立链接即可。

3.2.4方法区回收

3.3 垃圾收集算法

3.3.1. Mark-Sweep(标记-清除算法)

- 最基础的算法
- 最容易实现的算法
- 算法:分为两个阶段:标记阶段和清除阶段。
    - 标记阶段:就是标记出所有要被回收的对象。
    - 清除阶段:回收被标记的要被回收的内存区域。
- 最大缺点:容易产生内存碎片。
标记清除算法标记清除算法

3.3.2. Copying(复制算法)

- 为了解决Mark-Sweep算法的缺点。
- 算法:将内存按容量划分成大小相等的两块,每次只使用其中的一块。
    当内存用完了,就将还存活的对象复制到另一块内存上,然后将已使用的内存空间一次性清除掉,这样一来就容易出现内存碎片。
- 优点:算法简单,运行高效而且还不容易出现内存碎片。
- 缺点:内存使用代价高,因为能够使用的内存只有原来的一半。
Copy算法Copy算法

3.3.3. Mark-Compact(标记-整理算法)

- 为了解决Copy算法,充分利用内存空间。
- 算法:标记阶段和Mark-Sweep算法一样,但是完成标记之后,他不是直接清除被标记的内存区域,而是将存活对象往一端移动,然后清除端外的区域。
Mark-Compact算法Mark-Compact算法

3.3.4.Generational Colletion (分代收集算法)

- 目前大多数jvm采用的算法
- 算法核心思想:根据对象的存活周期将内存划分为若干个区域。
    一般情况下将堆内存分为:新生代和老年代。
        - 新生代特点:每次内存垃圾回收时,有大量内存对象要被回收。
        - 老年代特点:每次垃圾回收时,只有少量对象需要被回收。
- 目前大多数垃圾收集器:
    - 对于新生代都采用Copying算法。
        因为新生代每次都有大量对象被回收,也就是说复制的次数较少,但是实际中并不是按1:1来划分新生代的内存空间。
        一般将新生代划分为一块较大的Eden区和两块较小的Survivor区(一般为8:1:1),每次使用Eden区和一块Survivor区。当进行回收时,一般都将Eden区和Survivor中还存活的对象复制到另一块Survivor中,然后清除Eden区和刚才使用过的Survivor区。
    - 对于老年代都采用Mark-Compact算法。
        因为老年代每次回收都只有少量对象回收。

3.4 hotspot算法的实现

3.4.1枚举根节点

3.4.2安全点

3.4.3安全区

3.5 垃圾收集器

参考资料:
https://www.ibm.com/developerworks/cn/java/j-lo-JVMGarbageCollection/

3.5.1 serial收集器(新生代串行收集器)

串行收集器运行示意图串行收集器运行示意图

3.5.2 ParNew 收集器

ParNew收集器运行示意图ParNew收集器运行示意图

就是Serial收集器的多线程版本.

3.5.3 parallel scavenge 收集器(新生代并行回收收集器)“吞吐量优先收集器”

概念:
并行:指多条垃圾线程在并行工作,用户线程处于等待状态.
并发:垃圾收集线程和用户线程同时运行,垃圾线程在另一个CPU上运行.

3.5.4 serial old 收集器(老年代串行收集器)

老年代串行收集器老年代串行收集器

3.5.5 parallel old 收集器(老年代并行回收收集器)

Prallel Scavenge / Parllel Old 收集器运行示意图Prallel Scavenge / Parllel Old 收集器运行示意图

3.5.6 cms 收集器

Concurrent Mark Sweep


Concurrent Mark SweepConcurrent Mark Sweep

3.5.7 g1 收集器

  1. 面向服务器端."标记-整理"
  2. 分代收集:不需要配合其他GC收集器就能管理整个GC堆。
  3. 空间整合
  4. 可预测的停顿
image_1bb13kfrh1l1l1puio61okhmqu9.png-27.9kBimage_1bb13kfrh1l1l1puio61okhmqu9.png-27.9kB

理解gc日志

垃圾收集器参数总结

5、Java堆内存开关

Java堆内存开关Java堆内存开关

http://p3.pstatp.com/large/f740004e7f539099df0 image_1b6ha87jm1qfv1fc5kvj1vnr13qv9.png-364kBimage_1b6ha87jm1qfv1fc5kvj1vnr13qv9.png-364kB

第三部分.虚拟机执行子系统

  1. 类文件结构
  2. 虚拟机类加载机制
  3. 虚拟机字节码执行引擎
  4. 类加载及执行子系统的案例与实战

7 虚拟机类加载机制

7.1 类加载的时机

类的生命周期类的生命周期

加载、验证、准备、初始化、卸载 这五个步骤的顺序是确定的.
但是,解析 这一步可能在初始化之后完成,这是为了指出Java语言的运行时绑定.

什么情况下需要开始类加载的第一个阶段:加载?
1)new实例化对象的时候、读取或设置一个静态字段的时候、调用一个类的静态方法的时候
2)反射调用的时候.
3)初始化一个类的时候,如果发起其父类还没有被初始化的的时候,先触发父类的初始化方法
4)
5)

7.2 类加载的过程

类加载的过程:加载、验证、准备、解析、初始化

1)加载:
查找并加载类的二进制数据
2)链接:
验证:确保被加载的类符合JVM规范、没有安全性方面的问题.
准备:为静态变量分配内存,并将其初始化为默认值.
解析: 把虚拟机常量池中的符号引用转换为直接引用.
3)初始化
为类的静态变量赋予正确的初始化值.

7.2.1 加载

在加载阶段,虚拟机主要完成以下3件事:
1. 通过一个类的全名获取定义此类的二进制字节流;
2. 将这个字节流所代表的静态数据结构转换成方法区的运行时数据结构;
3. 在内存中生成一个代表该类的Class对象,作为方法区这个类的各种数据的访问入口.

7.2.2 验证

7.3 类加载器

7.3.1 类与类加载器

7.3.2 双亲委派模型

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            //1.检查请求的类是否已经被加载过了
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                    // 如果父类为null,调用父类的加载器
                        c = parent.loadClass(name, false);
                    } else {
                    //如果父类不为null,则调用bootstap的类加载器
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    //如果父类加载器抛出ClassNotFoundException
                    //说明父类加载器无法完成加载请求
                }

                if (c == null) {
                    // 在父类加载器无法加载的时候
                    // 再调本身的findClass方法来进行类加载
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

7.3.3 破坏双亲委派模型


第四部分.程序编译与代码优化


第五部分.高效并发

12.java内存模型与线程安全

12.1. 主内存和工作内存

- 内存模型的主要目标:定义程序中各个变量的访问规则。
- 线程对变量的所有操作(读取、赋值等)都在工作内存中进行,不能再主内存中操作。
- 线程间变量值的传递均需要通过主内存来完成。
- 虚拟机可能会让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问读写的是工作内存。
线程、主内存、工作内存三者的交互关系线程、主内存、工作内存三者的交互关系

12.2. 内存间的相互操作

12.3.volatile型变量的特殊规则

1,当一个变量的定义为volatile变量之后,此变量对所有线程是可见的。
    - 基于volatile变量的运算在并发下是安全的???
    java里面的运算并非原子操作,所以volatile变量在并发条件下也是不安全的。
2. 禁止指令重排序优化。

12.4.先行发生原则(happen-before原则)

13.线程安全与锁优化


问题

  1. PSYoungGen/PSOldGen/PSPermGen区别

    PS是Paraller Scavenge的简称。
    PSYoungGen:新生代
    PSOldGen:老年代
    PSPermGen:永久代

    • SUN JVM GC 使用是分代收集算法,即将内存分为几个区域,将不同生命周期的对象放在不同区域里.
      新的对象会先生成在Young area,也就是PSYoungGen中
      在几次GC以后,如过没有收集到,就会逐渐升级到PSOldGen 及Tenured area(也就是PSPermGen)中。
    • 在GC收集的时候,频繁收集生命周期短的区域(Young area),因为这个区域内的对象生命周期比较短,GC 效率也会比较高。而比较少的收集生命周期比较长的区域(Old area or Tenured area),以及基本不收集的永久区(Perm area)。

参考

  1. Java 内存模型及GC原理
  2. java 内存垃圾回收模型
上一篇下一篇

猜你喜欢

热点阅读