(一)java的内存模型

程序计数器(私有)

  程序计数器:"是一个非常小的内存空间,用来保证程序依次执行",它可以看作是当前线程所执行的字节码的行号指示器 由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在切出后切回的时候需要一个标识。

栈 (私有) "基本类型、运算、方法服务、指向堆内存的指针"

  虚拟机栈(‘执行的是java方法服务‘) 它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧 (Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。‘每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。‘ 局部变量表: 存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型, 它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。 ‘异常抛出‘ 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果Java虚拟机栈容量可以动态扩展,‘其实HotSpot虚拟机的栈容量是不可以动态扩展的‘ 当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。 本地方法栈(‘执行的是虚拟机用到的native方法服务‘) 一个native方法就是一个Java调用非Java代码的接口。 本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常。

堆 (共享)(-Xmx -Xms)("是垃圾收集器管理的主要区域。")

  "堆内存用于存放由new创建的对象和数组,集合等,String new 出来和不是new出来的存储地方不同。"保存所有引用数据的真实信息;存放对象实例,几乎所有的对象都是存在堆中的,被‘所有的线程共享。‘ 包括原始类型的封装类(如Byte、Integer、Long等等),‘不管对象是属于一个成员变量还是方法中的本地变量,它都会被存储在堆区。‘ 新生代和老年代 其中新生带存放新生的对象或者年龄不大的对象,老年代则存放老年对象。   新生代分为eden区、s0区、s1区,"s0和s1也被称为from和to区域,他们是两块大小相等并且可以互相角色的空间。"   绝大多数情况下,对象首先分配在eden区,在新生代回收后,如果对象还存活,则进入s0或s1区,之后每经过一次   新生代回收,如果对象存活则它的年龄就加1,对象达到一定的年龄后,则进入老年代。

堆栈的存储

  1、基础数据类型直接在栈空间分配;

  2、方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收;

  3、引用数据类型,需要用 new 来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量;

  4、方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收;

  5、局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待 GC 回收;

  6、方法调用时传入的实际参数,先在栈空间分配,在方法调用完成后从栈空间释放;

  7、字符串常量在 DATA 区域分配 , this 在堆空间分配;

  8、数组在栈空间分配数组名称, 在堆空间分配数组实际的大小!

  9.‘Static类型的变量以及类本身相关信息都会随着类本身存储在堆区。

  10.一个本地变量也有可能是一个对象的引用,这种情况下,这个本地引用会被存储到栈中,但是对象本身仍然存储在堆区。

  11.对于一个对象的成员变量,不管它是原始类型还是包装类型,都会被存储到堆区。

  12.堆中的对象可以被多线程共享。如果一个线程获得一个对象的应用,它便可访问这个对象的成员变量。如果两个线程同时调用了同一个对象的同一个方法, 那么这两个线程便可同时访问这个对象的成员变量,但是对于本地变量,每个线程都会拷贝一份到自己的线程栈中。

方法区(共享)(描述为堆的一个逻辑部分)"所以定义的方法的信息都保存方法区中"

  用于存储已被虚拟机‘加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。‘ 也就是所有编译器能够被确定,能够被快速查找的内容都存放在这里,它像数组一样通过索引访问,就是专门用来做查找的" ‘元空间(替代永久代)‘ 元空间并不在虚拟机中,而是"使用本地内存"。 -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值; 如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。 -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。 存储的是每个class的信息: 1.类加载器引用(classLoader) 2.运行时常量池(是方法区的一部分,用于存储编译器生成的各种字面量和符号引用。) 所有常量、字段引用、方法引用、属性 3.字段数据 每个方法的名字、类型(如类的全路径名、类型或接口) 、修饰符(如public、abstract、final)、属性 4.方法数据 每个方法的名字、返回类型、参数类型(按顺序)、修饰符、属性 5.方法代码 每个方法的字节码、操作数栈大小、局部变量大小、局部变量表、异常表和每个异常处理的开始位置、结 束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引 为什么要使用元空间能代替永久代 1、字符串存在永久代中,容易出现性能问题和内存溢出。   2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。   3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。 方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。

运行时常量池

  有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,‘总之就是装载class文件。‘ 这部分内容将在类加载后存放到方法区的运行时常量池中。

常量池

  常量池主要可以分为以下几种: (1)静态常量池:即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串/数字这些字面量,还包含类、方法的信息,占用class文件绝大部分空间。 这种常量池主要用于存放两大类常量:字面量和符号引用量,字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等;符号引用则属于编译原理    方面的概念,包括了如下三种类型的常量:类和接口的全限定名、字段名称描述符、方法名称描述符。         类的加载过程中的链接部分的解析步骤就是把符号引用替换为直接引用,即把那些描述符(名字)替换为能直接定位到字段、方法的引用或句柄(地址)。 (2)运行时常量池:虚拟机会将各个class文件中的常量池载入到运行时常量池中,即编译期间生成的字面量、符号引用,总之就是装载class文件。 字符串常量池 :字符串常量池可以理解为运行时常量池分出来的部分。加载时,对于class的静态常量池,如果字符串会被装到字符串常量池中。 整型常量池:Integer,类似字符串常量池,可以理解为运行时常量池分出来的。加载时,对于class的静态常量池装的东西,如果是整型会被装到整型常量池中。                类似的还有Character、Long等等常量池(基本数据类型没有哦)。。。

直接内存

  不在虚拟机中,称作堆外内存

原文地址:https://www.cnblogs.com/tianxin945/p/12359458.html

时间: 2024-10-18 17:16:13

(一)java的内存模型的相关文章

Java虚拟机内存模型及垃圾回收监控调优

Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存模型         正如你上图所看到的,JVM内存可以划分为不同的部分,广义上,JVM堆内存可以划分为两部分:年轻代和老年代(Young Generation and Old Generation) 年轻代(Young Generation) 年轻代用于存放由new所生成的对象.当年轻代空间满时,

java String 内存模型

关于java的内存模型,参照以下的一篇文章: https://isudox.com/2016/06/22/memory-model-of-string-in-java-language/

java线程内存模型,线程、工作内存、主内存

转自:http://rainyear.iteye.com/blog/1734311 java线程内存模型 线程.工作内存.主内存三者之间的交互关系图: key edeas 所有线程共享主内存 每个线程有自己的工作内存 refreshing local memory to/from main memory must  comply to JMM rules 产生线程安全的原因 线程的working memory是cpu的寄存器和高速缓存的抽象描述:现在的计算机,cpu在计算的时候,并不总是从内存读

浅析Java的内存模型

一.前言 ??之前过年在家,空闲时间比较多,想要了解一下JVM相关的内容,于是买了<深入理解Java虚拟机>这本书,过了一遍其中的基础知识.时隔多日,都忘得差不多了.为了重新捡起来,我决定复习一遍,并编写相关的系类博文加深印象,这是第一篇,来讲一讲JVM最基础的内容--Java的内存模型. 二.正文 ?2.1 Java内存分布 ??Java的内存主要分为五个部分: 程序计数器: Java虚拟机栈: 本地方法栈: 堆内存: 方法区: ??具体结构如下图所示: 2.2 程序计数器 ??首先看第一部

关Java的内存模型(JMM)

JMM的关键技术点都是围绕着多线程的原子性.可见性和有序性来建立的 一.原子性(Atomicity) 原子性是指一个操作是不可中断的.即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰. 比如,对于一个静态全局变量int i,两个线程同时对它赋值,线程A给他赋值1,线程B给它赋值为-1.那么不管这2个线程以何种方式.何种步调工作,i的值要么是1,要么是-1.线程A和线程B之间是没有干扰的.这就是原子性的一个特点,不可被中断. 但如果我们不使用int型而使用long型的话,可能

java的内存模型

java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果.在此之前,主流程序语言(如C/C++等)直接使用物理硬件和操作系统的内存模型,因此,会由于不同平台上内存模型的差异,有可能导致程序在一套平台上并发完全正常,而在另外一套平台上并发访问却经常出错,因此在某些场景下就不许针对不同的平台来编写程序.Java内存模型即要定义得足够严谨,才能让Jav

Java 堆内存模型

堆内存 Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象. 在 Java 中.堆被划分成两个不同的区域:新生代 ( Young ).老年代 ( Old ).新生代 ( Young ) 又被划分为三个区域:Eden.From Survivor.To Survivor. 这样划分的目的是为了使 JVM 可以更好的管理堆内存中的对象.包含内存的分配以及回收. 堆的内存模型大致为: 从图中能够看出: 堆大小 = 新生代 + 老年代.当中,堆的大小能够通过參数 –Xms

java 的内存模型

内存模型: 为了屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发结果.

JAVA的内存模型及结构

所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢? Java内存模型 Java内存模型在JVM specification, Java SE 7 Edition, and mainly in the chapters “2.5 Runtime Data Areas” and “2.6 Frames”中有详细的说明.对象和类的数据存储在3个不同的内存区域:堆(heap space).方法区(m

浅谈Java的内存模型以及交互

本文的内存模型只写虚拟机内存模型,物理机的不予描述. Java内存模型 在Java中,虚拟机将运行时区域分成6中,如下图:              程序计数器:用来记录当前线程执行到哪一步操作.在多线程轮换的模式中,当当前线程时间片用完的时候记录当前操作到哪一步,重新获得时间片时根据此记录来恢复之前的操作. 虚拟机栈:这就是我们平时所说的栈了,一般用来储存局部变量表.操作数表.动态链接等. 本地方法栈:这是另一个栈,用来提供虚拟机中用到的本地服务,像线程中的start方法,JUC包里经常使用的