JVM的学习1_____内存模型

时间:2019-08-21
本文章向大家介绍JVM的学习1_____内存模型,主要包括JVM的学习1_____内存模型使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

         前言:在学习Java第一课时,老师就讲到:Java不同于C/C++的手动内存分配与回收。原来这都得益于JVM的内存自动管理机制,但是在这背后又暗藏什么玄机呢???本人从图书馆借来了《Java虚拟机精讲》来一探究竟。

一.JVM的内存模型:

如下图所示可以分为5个模块:堆,栈,本地方法栈,PC寄存器,方法区。

这些内存区域用来存储程序运行时的数据。

根据线程访问的权限不同,其中线程共享的内存区域:堆,方法区,运行时常量池;线程私有的内存区域:栈,本地方法栈,PC寄存器。

1.线程共享的内存区域(堆区,方法区,运行时常量池):

1.堆(heap): 

      在JVM启动的时候创建该区域,堆区在实际的物理内存中是可以不连续的。我们程序中所创建的对象实例,和数组都存放在堆区,所以堆区是GC(垃圾回收器)的高频工作地点,但是当回收和使用大的内存区域时,可能会出现性能瓶颈,这时我们就提出疑问对象一定要放在堆区吗???当然是可以不放在堆区。对此我们有两套解决方案:逃逸分析,栈上分配以及TaoBaoVM都是可以将对象放在堆区之外的解决方案,用来提升GC的效率。

      由于Java中对象的生命周期不同,有的对象生命周期非常短暂,有的对象声明周期甚至和JVM的生命周期一样,这就使得要采用不同的GC算法来收集不同类型的对象。由此分代回收算法诞生!目前几乎所有的GC算法都是分代收集算法。

      堆区的内存区域又可细分为:新生代(Young Gen),年老代(Old Gen)其中新生代又可按8:1:1分为Eden,FromSurvivor,ToSurvivor。堆区的参数设置大小在JVM启动的时候就已经设置好了,可以通过 -Xms:获得堆区的起始大小; -Xmx:获得堆区的最大大小。当超出-Xmx(堆区的最大值)就会抛出OOM(out of memory)Error。

//一个OOM的代码实例
public class OOMTest {
    public static void main(String [] args){
        List<OOMObject> list=new ArrayList<>();
        while (true){
            list.add(new OOMObject());
            System.out.println("创建了一个静态对象!");
        }
    }
    static class  OOMObject{

    }
}

2.方法区:

       方法区和堆区是一样的,也是属于线程共享的区域。方法区中存储了每一个Java类的结构信息,比如:运行时常量池,字段,方法数据,以及构造函数和普通方法的字节码内容,和类,实例,接口初始化时所用到的特殊方法等数据。在HotSpot(是sunJDK,openJDK所带的JVM,也是目前使用最广的Java虚拟机)的实现中方法区的实际的物理内存位于堆中,也就是说方法区只是逻辑上的独立。

      方法区也被称为永久代,因为方法区并不会象堆区那样进行频繁的GC,甚至还可以设置参数让GC不回收方法区,若GC收集方法时也只是收集运行时常量池和类型卸载。方法区可通过-XX:MaxPermSize设置内存大小进行动态内存扩展。

     方法区也会发生内存溢出,当内存大小超过-XX:MaxPermSize时就会抛出OOMError。

3.运行时常量池:

     运行时常量池属于方法区,一个有效的字节码文件中除了包含字段,方法,接口等信息外,还应包括常量池表,运行时常量池就是常量池表运行时的表示形式,运行时常量池中可以存放多种不同的常量。

    当类加载器成功的将一个类或者接口加载进JVM,就会分配相应的运行时常量池,由于运行时常量池位于方法区中,故也会发生OOMError

2.线程私有的内存区域(PC寄存器,栈,本地方法栈):

1.pc寄存器:

     pc寄存器不同于物理寄存器,更像是一个pc计数器,其生命周期和线程的生命周期保持一致。PC寄存器记录了当前字节码指令地址;

     pc寄存器是唯一一个不会发生OOMError的内存区域。

2.Java栈:

     栈区的生命周期和线程的生命周期一样,栈主要用来存储栈帧,栈帧中存储了局部变量表,操作数栈,以及方法出口等信息。Java堆中存的是对象实例,那么Java栈的局部变量表中存放的是各类原始数据类型,对象引用,以及returnAddress(是JVM内部的原始数据类型)类型。

    Java栈的大小,可以设置为固定大小,也可设置为动态的大小。当线程请求分配的栈容量超过Java栈的最大大小JVM就会抛出StackOverflowError异常。

3.本地方法栈:

    用于支持本地方法的执行,并不是必须要的一块内存区域。同样也会发生OOM和StackOverFlow。

lk

原文地址:https://www.cnblogs.com/xbfchder/p/11391585.html