zoukankan      html  css  js  c++  java
  • Java内存模型

    内存模型分类

    • 程序计数器

      是线程私有的。Java虚拟机的多线程是通过线程的轮流切换并分配处理器时间来实现的,在任何一个时刻,一个处理器或者一个内核只能处理一个县城,为了保证每个线程能正确的回到之前执行的位置,所以引入程序计数器。

      字节码解释器工作时就是通过改变这个程序计数器的值来选取下一个需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复都是依据它完成的。

      记录内容:如果线程执行的是Java方法,那么记录的就是虚拟机字节码指令的地址;如果执行的是native方法,记录的就是空。

    • 虚拟机栈

      线程私有,生命周期和线程一致。记录内容:每个方法执行时都会生成一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口。

      局部变量表的理解:

        局部变量表的内存空间在编译器就分配好了,在方法运行期间不会改变其大小。主要存的是编译期可知的基本数据类型,对象的引用。

    • 本地方法栈

      线程私有的。主要存的是native方法相关的信息,主要为本地方法服务。

    • 虚拟机堆

      线程公用的。虚拟机中几乎所有的对象实例都放在这里面,其实垃圾回收管理的重点区域,也是jvm调优重点关注的区域。

      虚拟机堆介绍:

        主要分为新生代和老年代。新生代又分为Eden区,from survivor区,to sruvivor区,Eden区和survivor区打大小默认是8:2,可以通过

      但从内存分配来看,线程共享的Java堆可能会划分出多个线程私有额分配缓冲区,这样做的目的是为了更好的回收内存,或者更快的分配内存。

      Java堆可以处在物理上不连续的内存空间中,但是逻辑上一定要是连续的。而在给某个实例对象分配堆空间时一定要是连续的空间,所以这里就牵涉到内存回收算法(例如复制内存,,标记清除,标记整理)。可以通过xmx和xms来设置最大对内存和最小对内存。  

    • 方法区

      线程共享的。用于存储已被虚拟机加载的类信息(类的版本,字段,方法,接口等描述信息,虚拟机对class文件每个部分的格式,以及每个字节存放哪种数据都有严格的规定),常量,静态变量,即时编译后的代码。方法去可以是物理上不连续额的空间,但是逻辑上要是连续的,也可以选择固定大小的或者可扩展的内存,还可以选择要不要实现垃圾收集。

    一般垃圾收集行为在方法去是比较少的,但是这个区域的内容也有可能被回收:主要是针对对常量池的回收(废弃额常量)和对类型的卸载(符合回收标准的类)。

    • 运行时常量池

      其实方法区的一部分。用于存放编译期生成各种字面常量和符号引用,是在类加载后放到这里的。运行时常量池是动态的,除了编译期的数据可以存进来之外,运行期也可以加入新的常量放入池中。

    对象的创建

      虚拟机在接受到一个new指令的时候,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载解析初始化,如果没有,必须限制性相应的类加载过程。

      在类加载检查通过后,虚拟机将会为新生的对象分配内存空间。对象所需的内存空间在类加载完毕后就可以完全确定,所以给新对象分配内存空间就是在Java堆内存中划分一块确定大小的内存区域。当然对内存空间是否规整,也就是已经使用的内存和未使用的内存是否交错存在和垃圾回收器是否带有压缩整理功能有关。如果对内存是规整的也就是使用的内存在一边没使用的内存在另一边,在他们中间会有一个指针作为指示器,当需要分配内存空间时,指针就往空闲区域那边移动特点的距离,这种方式叫做“指针碰撞”。那么在并发环境中,同事发生“指针碰撞”怎么解决呢?1.对分配的内存空间的动作进行线程同步(通过虚拟机cas配上失败充实的方式);2.预先在对内存中为每个线程分配一块小区域称为“本地线程分配缓冲区(TLAB)”。哪个线程需要分配对内存时就在缓冲区分配,只有在缓冲区用完并分配新的缓冲区时才需要同步锁定。是否使用TLAB可以用过-XX:+/-UseTLAB参数来设定。

       内存分配完成后,虚拟机需要把这块空间初始化为零值,如果使用TLAB的话这个过程也可以提前到TLAB分配时进行。这主要是保证实例字段在Java代码中可以不赋初始值就可以使用。

      然后虚拟机要对对象进行必要的设置,例如对象是哪个类的实例,如果找到对象的元数据信息,对象的哈希码,对象GC分代的年龄等。这些信息存放在对象的对象投中。

    然而堆内存分为不同代,对象按什么存储规则存到对内存中呢?

      首先新被创建的对像,会被分配到新生代的Eden区,如过Eden区的内存大小不足以存新对象,会进行一次Gc,然后把让然存活的对像放到survivor区,如此循环此操作。

    什么对像会放到老年代呢?

      1.当对像的GC年龄大于默认值的时候会认为对像存活时间比较长,就放到老年代,-xx:MaxTenuringThreshold来设置

       2.相同GC年龄的对象超过survivor内存的一半时,则大于等于这个GC年龄的对象会放到老年代中

       3.新对象的大小如果大于Eden的最大内存时,会直接将对象放入老年代

       4.大对象,例如比较长的字符串或者数组,直接放入到老年代中,可以通过设置-XX:PretenureSizeThreshold这个值来设置

  • 相关阅读:
    IOS基于 fmdb数据库 的简单操作应用
    App 上线被拒绝的原因有哪些?
    cocoaPods的安装以及使用
    IOS开发工程师的近期面试题
    UIButton基本介绍
    UIView 详解
    使用 Paros 抓接口
    如何在main方法中创建50000次对象
    如何将将String 转换 int
    2020年09月24号--测试登录账号15分钟有效时长
  • 原文地址:https://www.cnblogs.com/htyj/p/8616477.html
Copyright © 2011-2022 走看看