JVM集训-----内存结构

一、程序计数器/PC寄存器 (Program Counter Registe

  用于保存当前正在执行的程序的内存地址(下一条jvm指令的执行地址),由于Java是支持多线程执行的,所以程序执行的轨迹不可能一直都是线性执行。当有多个线程交叉执行时,被中断的线程的程序当前执行到哪条内存地址必然要保存下来,以便用于被中断的线程恢复执行时再按照被中断时的指令地址继续执行下去。为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存,是线程安全的。

  特点:1.线程私有  2.不会存在内存溢出

二、虚拟机栈(Java Virtual Machine Stack)

  虚拟机栈总是与线程关联在一起的,每当创建一个线程,JVM就会为该线程创建对应的虚拟机栈,在这个虚拟机栈中又会包含多个栈帧(Stack Frame),这些栈帧是与每个方法关联起来的,每运行一个方法就创建一个栈帧,每个栈帧会含有一些局部变量、操作栈和方法返回值等信息。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,虚拟机栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向该地址。(只有一个活动栈)只有这个活动的栈帧的本地变量可以被操作栈使用,当在这个栈帧中调用另外一个方法时,与之对应的一个新的栈帧被创建,这个新创建的栈帧被放到Java栈的栈顶,变为当前的活动栈。同样现在只有这个栈的本地变量才能被使用,当这个栈帧中所有指令都完成时,这个栈帧被移除虚拟机栈,刚才的那个栈帧变为活动栈帧,前面栈帧的返回值变为这个栈帧的操作栈的一个操作数 

 2.1 特点:

  1. 每个线程包含一个栈区,栈中只保存方法中(不包括对象的成员变量)的基础数据类型和自定义对象的引用(不是对象),对象都存放在堆区中
  2. 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
  3. 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

 2.2 问题辨析:

  1.垃圾回收涉及到栈内存吗? 不涉及,栈内存中存放了一个个栈帧,当一个栈帧执行完时,该栈帧就会弹出虚拟机栈,然后被清空

  2.栈内存分配越大越好吗? 不是,总内存是一定的,当栈内存分配大了,最大线程数就会减少

  3.方法内的局部变量是否线程安全?

    (1).如果方法内局部变量没有逃离方法的作用访问,它是线程安全的

    (2).如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

    

  2.3 栈内存溢出

    原因:

    1.栈帧过多导致栈内存溢出

    2.栈帧过大导致栈内存溢出

    以上都属于线程请求的栈深度大于虚拟机所允许的深度,会报StackOverflowError异常,如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

三、本地方法栈(Native Method Stacks)

  本地方法栈和Java(虚拟机)栈所发挥的作用非常相似,区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行Native方法服务。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

  这俩都是JVM规范所规定的概念上的东西,并不是说具体的JVM实现真的要给每个Java线程开两个独立的栈。以Oracle JDK / OpenJDK的HotSpot VM为例,它使用所谓的“mixed stack”——在同一个调用栈里存放Java方法的栈帧与native方法的栈帧,所以每个Java线程其实只有一个调用栈,融合了JVM规范的JVM栈与native方法栈这俩概念

四、堆(Heap)

  堆是JVM所管理的内存中国最大的一块,是被所有Java线程锁共享的,不是线程安全的,在JVM启动时创建。堆是存储Java对象的地方,这一点Java虚拟机规范中描述是:所有的对象实例以及数组都要在堆上分配。Java堆是GC管理的主要区域,从内存回收的角度来看,由于现在GC基本都采用分代收集算法,所以Java堆还可以细分为:新生代和老年代;新生代再细致一点有Eden空间、From Survivor空间、To Survivor空间等

  4.1 特点:

  1.存储的全部是对象实例,每个对象都包含一个与之对应的class的信息(class信息存放在方法区)。

  2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身,几乎所有的对象实例和数组都在堆中分配。

  3.不需要连续的内存、可以选择固定大小、可扩展

  4.2 堆内存溢出

    当堆中没有可用内存完成实例分配,并且也无法再扩展时

      

  4.3 堆内存诊断

    1. jps 工具

       查看当前系统中有哪些 java 进程

    2. jmap 工具

        查看堆内存占用情况 jmap - heap 进程id

    3. jconsole 工具

        图形界面的,多功能的监测工具,可以连续监测

五、方法区

  又叫静态区,跟堆一样,被所有的线程共享。它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据(比如spring 使用IOC或者AOP创建bean时,或者使用cglib,反射的形式动态生成class信息等)。

  5.1 组成

  

  由上图可知,jdk1.6的时候,方法区是通过永久代实现的。而对于jdk1.8,方法区是通过元空间实现的。存储位置不同,永久代物理是是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存;

  注:JDK 6 时,String等字符串常量的信息是置于方法区中的,但是到了JDK 7 时,已经移动到了Java堆。

  5.2 方法区内存溢出

  永久代内存溢出  :java.lang.OutOfMemoryError: PermGen space

       -XX   :  MaxPermSize=8m

  

  元空间内存溢出  : java.lang.OutOfMemoryError: Metaspace

        -XX   :   MaxMetaspaceSize=8m

  5.3 常量池

  常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量 等信息

  5.4 运行时常量池

  运行时常量池是方法区的一部分,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量 池,并把里面的符号地址变为真实地址,一个类加载到 JVM 中后对应一个运行时常量池 

  5.5 字符串常量池(StringTable)

   5.5.1 注意:

    1. JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代
    2. 在JDK1.7 字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代
    3. 在JDK1.8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace)

   5.5.2 特性:

       1.常量池中的字符串仅是符号,第一次用到时才变为对象

      2.利用串池的机制,来避免重复创建字符串对象

      3.字符串变量拼接的原理是 StringBuilder (1.8)

      4. 字符串常量拼接的原理是编译期优化

      5.可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池

        1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

        1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份, 放入串池, 会把串池中的对象返回

      6.可被垃圾回收

      7.hashtable 的实现

   5.5.3 性能调优
      调整 -XX:StringTableSize=桶个数

  

     5.5.4 面试题

String s1 = "a";
String s2 = "b";
String s3 = "a" + "b";
 String s4 = s1 + s2;
 String s5 = "ab";
String s6 = s4.intern();
// 问
System.out.println(s3 == s4);
 System.out.println(s3 == s5);
System.out.println(s3 == s6);
String x2 = new String("c") + new String("d");
 String x1 = "cd"; x2.intern();
// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢
System.out.println(x1 == x2);

  共6个答案:false true true false  true false

六、直接内存(Direct Memory)

  NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可使用Native函数库直接分配堆外内存。不受Java堆大小(-Xmx)限制,从而可能造成各个内存区域总和大于物理内存限制而造成动态扩展时出现OutOfMemoryError。

  6.1 特点:

  1.常见于 NIO 操作时,用于数据缓冲区

  2.分配回收成本较高,但读写性能高

  3.不受 JVM 内存回收管理

  6.2 分配和回收原理

  1.使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法

  2. ByteBu?er 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBu?er 对象,一旦 ByteBu?er 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调 用 freeMemory 来释放直接内存

  

原文地址:https://www.cnblogs.com/lowerma/p/11929041.html

时间: 2024-08-30 02:00:32

JVM集训-----内存结构的相关文章

JVM的内存结构,JVM的回收机制

内存作为系统中重要的资源,对于系统稳定运行和高效运行起到了关键的作用,Java和C之类的语言不同,不需要开发人员来分配内存和回收内存,而是由JVM来管理对象内存的分配以及对象内存的回收(又称为垃圾回收.GC),这对于开发人员来说确实大大降低了编写程序的难度,但带来的一个副作用就是,当系统运行过程中出现JVM抛出的内存异常(例如OutOfMemoryError)的时候,很难知道原因是什么,另外一方面,要编写高性能的程序,通常需要借助内存来提升性能,因此如 何才能合理的使用内存以及让JVM合理的进行

JVM—Java内存结构

JAVA虚拟机内存 JAVA虚拟机内存,也叫JAVA内存,可以理解为Java虚拟机运行时数据区. JVM内存结构 包括: 线程共享的方法区 和 堆, 以及每个线程私有的 JAVA栈,本地方法栈 和 PC计数器(程序计数器). JAVA堆 堆,是JAVA虚拟机中所管理的内存中最大的一块,此内存区域的唯一目的就是存放对象实例以及数组. 所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制. JAVA堆与垃圾回收的渊源 JAVA堆也是垃圾回收器管理的主要区域,因此也被称为

JVM的内存结构

Java是按照运行时数据的存储结构来划分内存结构的,java虚拟机规范将java运行时的诗句划分为6种: 1.PC寄存器 用于保存当前执行程序的内存地址,同时java程序是多线程的,不可能一直都按照线性执行下去,当有多个线程交叉执行时,被中断的线程程序当前执行到哪条的内存地址需要记录下来. 2.java栈 java栈是与线程相关到一起的,每当创建一个线程时,JVM就会为这个线程创建一个java栈,这个hava栈中含有很多的栈帧,栈帧是与每个方法关联起来的,每运行一个方法就会创建一个栈帧.每个栈帧

浅析JVM内存结构和6大区域(转)

其实对于我们一般理解的计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据进行处理,又将数据保存到内存,通过分页或分片技术将内存中的数据再flush至硬盘.那JVM的内存结构到底是如何呢?JVM做为一个运行在操作系统上,但又独立于os运行的平台,它的内存至少应该包括象寄存器.堆栈等区域. JVM在运行时将数据划分为了6个区域来存储,而不仅仅是大家熟知的Heap区域,这6个区域图示如下: 下面将逐一介绍下各个区域所做的工作及其充当的功能.

浅析JVM内存结构和6大区域

其实对于我们一般理解的计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据进行处理,又将数据保存到内存,通过分页或分片技术将内存中的数据再flush至硬盘.那JVM的内存结构到底是如何呢?JVM做为一个运行在操作系统上,但又独立于os运行的平台,它的内存至少应该包括象寄存器.堆栈等区域.JVM在运行时将数据划分为了6个区域来存储,而不仅仅是大家熟知的Heap区域,这6个区域图示如下: JVM内存的分配结构示意图下面将逐一介绍下各个区域所

java中OutofMemoryError和JVM内存结构

OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决: 1:是排查程序是否有BUG导致内存泄漏: 2:是调整JVM启动参数增大内存. OutOfMemoryError有好几种情况,每次遇到这个错误时,观察OutOfMemoryError后面的提示信息,就可以发现不同之处,如: 引用 java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: unable t

JVM内存结构和6大区域

其实对于我们一般理解的计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据进行处理,又将数据保存到内存,通过分页或分片技术将内存中的数据再flush至硬盘.那JVM的内存结构到底是如何呢?JVM做为一个运行在操作系统上,但又独立于os运行的平台,它的内存至少应该包括象寄存器.堆栈等区域. JVM在运行时将数据划分为了6个区域来存储,而不仅仅是大家熟知的Heap区域,这6个区域图示如下: JVM内存的分配结构示意图 下面将逐一介绍下各个区

JVM的基本结构和JVM内存结构

JVM的基本结构和JVM的内存结构 这里介绍一下JVM在启动后,作为操作系统的一个进程的基本结构,以及从操作系统角度看,JVM如何管理它从操作系统里申请来的内存的,也就是JVM的内存结构或者叫JVM内存模型. 1.JVM的基本结构 ================ JVM启动后,对操作系统来说,JVM是一个的进程,这个进程的基本结构如上图所示.它包括:类加载器子系统.运行时数据区.执行引擎和本地方法接口. 运行时数据区是JVM从操作系统申请来的堆空间和操作系统给JVM分配的栈空间的总称.JVM为

JAVA常见错误处理方法 和 JVM内存结构

OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏:二是调整JVM启动参数增大内存.OutOfMemoryError有好几种情况,每次遇到这个错误时,观察OutOfMemoryError后面的提示信息,就可以发现不同之处,如: java.lang.OutOfMemoryError: Java heap spacejava.lang.OutOfMemoryError: unable to create