凌霄的博客
深入理解java虚拟机-笔记
深入理解java虚拟机-笔记

今天开始拜读《深入理解java虚拟机》,做个笔记,避免自己忘记。

java运行时数据区域分为:

  1. 程序计数器:记录下一次需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖他。java虚拟机的多线程是通过线程轮流切换分配处理器执行时间的方式实现的,在一个确切的时间点,一个处理器都只会执行一条线程中的指令,所以为了线程能够切换到正常的位置,每个线程都有独立的程序计数器,这类内存区域也叫线程私有的内存。
  2. java虚拟机栈:也是线程私有的的。生命周期与线程相同。很多人把java内存分为堆内存和栈内存,java内存区域远比这个复杂,所指的栈就是这个,或者说是虚拟机栈中局部变量表的部分。局部变量表包括编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double),对象的引用,和返回值类型。如果线程请求的栈深度大于虚拟机设定的允许的深度,会抛出StackOverFlowError。如果虚拟机可以动态扩展,扩展时无法申请到足够的内存,就会抛出OutMemoryError。
  3. 本地方法栈:和上面不同的地方是虚拟机栈为java方法服务(也就是字节码)。本地方法栈为native方法服务,有的jvm是把这两者合二为一的,与虚拟机栈一样,本地方法栈也会抛出StackOverFlowError和OutMemoryError。
  4. java堆:是所有线程共享的内存区域。唯一的目的就是存放对象实例,几乎所有的对象实例都是在这里分配内存。java堆是垃圾收集器管理的主要区域,所以很多时候也被叫做GC堆。需要注意的是,java堆可以使固定的,也可以是扩展的,当前很多流行的虚拟机都可以扩展(通过-Xmx和-Xms控制,终于知道当android studio卡顿时设置的那对参数了),如果在堆内存中没有内存完成实例分配,并且堆内存无法扩展,就会抛出OutMemoryError。
  5. 方法区:和java堆一样,是所有线程共享的内存区域。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。这区域可以选择不实现垃圾收集。内存回收的主要目标是针对常量池的回收和对类型的卸载。方法区无法满足内存分配时,会抛出OutMemoryError。
  6. 运行常量池:方法区的一部分,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
  7. 直接内存:并不是jvm运行时数据区的一部分,因为这部分内存被频繁的使用,也可能导致OutMemoryError,所以就拿出来讲,在jdk1.4时引入了nio(New Input?Output)类,它可以使用native函数库直接分配堆外内存,避免了在java堆和Native堆来回复制数据,显著提高性能。因为是直接内存,如果设置了虚拟机参数内存信息,各个内存之和+直接内存可能会大于实际的物理内存,就会造成OutMemoryError。

贴上第二个会出现异常的代码:

堆栈溢出错误一般是递归调用,原则上循环嵌套次数本身是没有限制的,限制的是占用的栈空间,如果你的函数里定义了很多很多变量,栈空间就会用完得比较快。 层数达到了环境的设定值,就会抛异常。

public class StackOverflowTest {
    public static void main(String[] args) {
        method();
    }
    public static void method(){
        for(;;)
            method();
    }
}

内存溢出:

public class OutOfMemoryTest {
    public static void main(String[] args){
        List list=new ArrayList();
        for(;;){
            int[] tmp=new int[1000000];
            list.add(tmp);
        }
    }
}

对象的创建

  1. jvm遇到了一条new指令
  2. 检查这个指令的参数能否在常量池中定位到一个类的引用以及是否已经被加载、解析和初始化过(类加载检查)
  3. 为新生对象分配内存
  4. 方法执行,把对象按照程序猿的意愿进行初始化

对象的内存布局

对象在内存中存储的布局可以分为三块区域:

  1. 对象头:又分为两个部分,第一个部分存放对象自身的运行时数据,比如哈希码、锁状态等,第二部分是类型指针,即对象指向它类的元数据的指针,虚拟机通过该指针确定这个对象是哪个类的实例。
  2. 实例数据:对象真正存储的有效信息,就是在程序中所定义的各种类型的字段内容。
  3. 对齐填充:不是必然存在的,也没有特别含义,只是占位的作用。有些虚拟机的对象大小必须为8的倍数,而对象头刚刚好是8的倍数,如果实例数据不是8的倍数,那就需要填充空白部分。

3.垃圾收集器与内存分配策略

对象存活算法

  1. 引用计数:给对象中添加一个引用计数器,当有一个地方引用它时,计数值就加1,引用失效时,计数值就减1,如果计数器的值为0则说明对象是不可能再被使用了。虽然效率很高,但是主流java虚拟机并没有使用它来管理内存,最主要的原因是它很难解决对象之间的相互循环引用问题。
  2. 可达性分析算法

发表评论

textsms
account_circle
email

深入理解java虚拟机-笔记
今天开始拜读《深入理解java虚拟机》,做个笔记,避免自己忘记。 java运行时数据区域分为: 程序计数器:记录下一次需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基…
扫描二维码继续阅读
2018-09-02