首页 > 系统相关 >JVM 内存区域

JVM 内存区域

时间:2024-08-20 19:51:27浏览次数:12  
标签:Java 虚拟机 区域 线程 内存 JVM 方法

一、JVM 简介

【概述】

JVM是Java虚拟机(Java Virtual Machine)的简称,是一种用于计算设备的规范,是一个虚构出来的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。我们学习使用的基本都是HotSpot虚拟机。

【优点】

优点

说明

实现跨平台运行

让底层代码和运行环境分离开,编写好一份代码后,不用再次修改内容,只用通过安装不同的JVM环境自动进行转换即可运行,在各种系统中无缝连接。

自动内存管理

专门设计了垃圾回收机制,来自动进行内存的管理,极大的优化了操作。

数组下标越界检查

提供了数组下标越界的自动检查机制,在检测到越界后,会在运行时自动抛出ArrayIndexOutOfBoundsException

多态

通过相同接口,不同的实例进行实现,完成不同的业务操作

二、JVM 内存区域

(一) 简图

(二) 各区域详解

1. Java虚拟机栈

【概述】

Java虚拟机栈绝对算的上是 JVM 运行时数据区域的一个核心,除了一些Native方法调用是通过本地方法栈实现的(下面会提到),其他所有的Java方法调用都是通过虚拟机栈来实现的。

【特点】

  1. 线程私有,生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡,不存在垃圾回收问题;
  2. Java方法调用的数据需要通过虚拟机栈栈进行传递;
  3. 每一次方法调用都会有一个对应的栈帧被压入栈中(先进后出);
  4. 每一个方法调用结束后,都会有一个栈帧被弹出(后进先出);
  5. 栈由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法返回地址。
  6. 每个方法执行的同时会创建一个栈帧,一个方法就对应一个栈帧

Java方法有两种返回函数的方式,不管使用哪种方式,都会导致栈帧被弹出。

  1. 正常的函数返回,使用return指令;
  2. 抛出异常(没有try catch)。

【栈溢出】

  1. StackOverFlowError 若栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就抛出该错误。
  2. OutOfMemoryError 如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出该异常。

【栈帧】

这里参考《深入理解 Java 虚拟机》这本书,只是做了简单的总结,具体的细节想了解的可以去读。

名称

说明

局部变量表

用于存放方法参数和方法内部定义的局部变量,在 Java 程序被编译为 Class 文件时确定最大容量。

操作数栈

主要作为方法调用的中转站使用,用于存放方法执行过程中产生的中间计算结果。另外,计算过程中产生的临时变量也会放在操作数栈中。

动态链接

指向运行时常量池的方法引用

方法返回地址

方法正常退出或者异常退出的定义

在Java源文件被编译成字节码文件时,所有的变量和方法引用都作为符号引用(Symbilic Reference)保存在Class文件的常量池里。当一个方法要调用其他方法,需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。动态链接的作用就是为了将符号引用转换为调用方法的直接引用。

2. 本地方法栈

  • 和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。
  • 在HotSpot虚拟机中和Java虚拟机栈合二为一;
  • 本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息;
  • 方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现StackOverFlowErrorOutOfMemoryError两种错误。

3. PC寄存器

【概述】

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令。

【作用】

  1. 存指向下一条指令的地址,也即将要执行的代码。由执行引擎读取下一条指令;
  2. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
  3. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

【特点】

  1. 线程私有:若干个线程是交替执行的,在线程与线程之间切换的时候,需要记录每个线程执行到那一步了;
  2. 唯一个在Java虚拟机规范中没有规定任何OutOtMemoryError情况的区域;
  3. 它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

4. 堆

【概述】

Java虚拟机所管理的内存中最大的一块,Java堆是所有线程共享的一块内存区域,在虚拟机启动时创建。

【特点】

  1. 此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存;
  2. Java堆是垃圾收集器管理的主要区域,因此也被称作GC堆;
  3. JDK 1.7 开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被使用,则可以直接在栈上分配内存。
  4. 默认情况下,JVM分配的总内存是电脑内存的

    ,初始化的内存是电脑内存的

【空间分类】

  • 新生代
    • Class诞生和生长的地方,甚至死亡;
    • Eden所有的对象都是在Edennew出来的;
    • SurvivorSurvivor分1区和2区,两者是动态的。
  • 老年代
    • 对象年龄达到阈值后进入老年代:默认情况下,对象在新生代经历了15次GC后;
    • 大对象直接进入老年代:通过以下JVM参数进行设置:-XX:PretenureSizeThreshold=5242880

原因:

  1. 大对象需要连续的内存空间,而新生代为了安放大对象可能需要多次进行GC,增加开销;
  2. 新生代种伊甸园区和幸存者区常采用复制算法,需要经常复制对象到不同的区域,而大对象在复制时开销较大。
    • 动态地根据对象的年龄以及新生代空间使用情况选择对象进入老年代。

HotSpot虚拟机并不一定会严格按照设置的年龄阈值,满足以下条件也能直接进入老年代:

Survivor区中,年龄从 1 到 n 的对象大小之和超过Survivor区的50%时,新生代中年龄大于等于 n 的对象将进入老年代。(这个对象大小总和是按年龄从小到大累加的,并不是同龄对象!)

  • 永久代(元空间)
    • 这个区域用来存放JDK自身携带的Class对象;
    • 存放Interface元数据,主要是Java运行时的一些环境或类信息;
    • 该区域不存在垃圾回收?关闭虚拟机就会释放这个区域的内存。
    • JDK1.8以后,永久代被元空间替代

【大致过程】

对象都会首先在Eden区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入S0或者S1,并且对象的年龄还会加 1(Eden区->Survivor区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold来设置。

【内存溢出】

  1. java.lang.OutOfMemoryError: GC Overhead Limit Exceeded 当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。
  2. java.lang.OutOfMemoryError: Java heap space假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发此错误。(和配置的最大堆内存有关,且受制于物理内存大小)

5. 方法区

【概述】

方法区别名叫做非堆(Non-Heap),目的就是要和堆分开,被所有线程共享。所有字段和方法字节码以及一些特殊方法,如构造函数、接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域上。但是实例变量存在堆内存中,和方法区无关。

【特点】

  1. 方法区和堆一样,是各个线程共享的区域;
  2. 方法区在JVM被启动时创建;
  3. 方法区的大小跟堆空间一样,可以选择固定大小或扩展;
  4. 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出溢出错误OutOfMemoryError
  5. 关闭JVM就会释放这个区域的内存。

【JDK1.8变化】

  • JDK1.6:方法区保存在内存结构中,叫做永久代,里面存储了运行时的常量池(包含串池StringTable)、类的信息、类加载器;
  • JDK1.8:方法区做为一个概念,保存在本地内存中,叫做元空间,里面存储了运行时的常量池、类的信息、类加载器,此时串池(StringTable)储存在堆之中。

【方法区和永久代以及元空间的关系】

方法区和永久代以及元空间的关系很像Java中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是方法区,也就是说永久代以及元空间是HotSpot虚拟机对虚拟机规范中方法区的两种实现方式。

6. native关键字

【概述】

native用来修饰方法,凡事带了native关键字的方法,说明Java的作用范围达不到了,需要去调C语言的库。

【语法】

  1. 修饰方法的位置必须在返回类型之前;
  2. 不能用abstract修饰,也没有方法体,也没有左右大括号;
  3. 返回值可以是任意类型;

【特点】

  1. 调用带native关键字的方法,该方法会进入本地方法栈;
  2. 本地方法栈会调用本地方法接口(JNI);

JNI作用:

通过JNI,我们就可以通过 Java 程序(代码)调用到操作系统相关的技术实现的库函数,从而与其他技术和系统交互,使用其他技术实现的系统的功能;同时其他技术和系统也可以通过 JNI 提供的相应原生接口开调用 Java 应用系统内部实现的功能。

【存在原因】

Java诞生的时候,CC++横行,要想立足必须调用他们的程序。所以Java专门在JVM的内存区域中开辟了一块标记区域本地方法栈,用来登记native方法,在最终执行的时候,通过本地方法接口(JNI)加载本地方法库中的方法。

【应用场景】

我们在日常编程中看到native修饰的方法,只需要知道这个方法的作用是什么,在企业级应用中很少见。一般涉及硬件系统相关开发可能会用到,比如Java程序驱动打印机。

7. 执行引擎


参考:

[1] 周志明. 深入理解 Java 虚拟机(第3版).

标签:Java,虚拟机,区域,线程,内存,JVM,方法
From: https://blog.csdn.net/m0_51607909/article/details/141333565

相关文章

  • vSphere5.9.1.管理内存和CPU分配
    第一部分管理内存和cpu分配关联vm内存虚拟机感觉自己拥有4g的内存,并且最多不会使用超过4g的物理内存.我们可以超额的关联内存给vm,例如:esxi主机的物理内存只有8g,但是我们可以给三个vm分配4g内存.esxi四大高级内存控制技术1.pagesharing(透明的页面共享)thefirstmemory......
  • mysql - 根据某经纬度 从区域列表内筛选符合条件的区域. 地图经纬度 坐标筛选
    作者原创.转载请注明来源我有一个区域列表.每个区域都有一堆经纬度坐标集合它们组成一个不规则图形.然后我有个经纬度坐标想筛选出这个坐标属于那个区域.mysql适合做这样的筛选吗?//创建区域坐标表CREATETABLEregions( idINTAUTO_INCREMENTPRIMARYKEY,......
  • 深入理解 Go 语言原子内存操作
            原子内存操作提供了实现其他同步原语所需的低级基础。一般来说,你可以用互斥体和通道替换并发算法的所有原子操作。然而,它们是有趣且有时令人困惑的结构,应该深入了解它们是如何工作的。如果你能够谨慎地使用它们,那么它们完全可以成为代码优化的好工具,而不会增加......
  • Java数组02:数组内存分析、三种初始化方式及特点
    本节内容视频链接:Java数组03:三种初始化及内存分析_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV12J41137hu?p=53&vd_source=b5775c3a4ea16a5306db9c7c1c1486b51.数组内存分析堆:存放new的对象和数组;可以被所有线程共享,不会存放别的对象引用;栈:存放基本变量类型,会包含......
  • C++ 获取Linux 服务器CPU占用率+内存空闲率(亲测绝对可以运行)
    转自:C++获取Linux服务器CPU占用率+内存空闲率(亲测绝对可以运行)-远征i-博客园(cnblogs.com)代码来自网络,部分修改,亲测绝对可用C++:#include<stdio.h>#include<stdlib.h>#include<string.h>#include<iostream>#include<unistd.h>usingnamespacestd;type......
  • TCPIP路由技术第一卷第八章OSPF 第五部分-1 特殊区域
    tcp/ip_ospf案例研究4特殊区域1.stub区域:区域内所有设备areaidstub:abr会通告一条oia(默认cost1)的默认路由,存在3类lsa不存在4,5类lsa2.totallystub区域:abr配置no-summary,其他设备配置areaidstub;abr会通告一条oia的默认路由;不存在3,4,5类lsa3.nssa区域:区域内所有......
  • C/C++语言基础--指针三大专题详解2(指针与数组关系,动态内存分配,代码均可)
    本专栏目的更新C/C++的基础语法,包括C++的一些新特性前言指针是C/C++的灵魂,和内存地址相关联,运行的时候速度快,但是同时也有很多细节和规范要注意的,毕竟内存泄漏是很恐怖的指针打算分三篇文章进行讲解,本专题是二,介绍了指针和数组的关系、动态内存如何分配和释放等问题专题......
  • 内存(动态开辟)———C语言
    内存管理: 1.C语言运行时的内存分配2.static关键字1.修饰变量局部变量:        <1>在编译的过程中,会在数据区为该变量开辟空间,如果代码中未对其进行初始化,则系统默认初始化为0。        <2>用static修饰的局部变量,会延长局部变量的生命周期#include<s......
  • idea低内存报错
    问题描述:解决办法:各种解决办法通过大模型搜索已经给出。由于各种原因,我们一般采用方法一修改内存配置解决。下边带大家手搓一遍:①通过设置菜单调整内存大小②编辑配置文件......
  • bash: 警告:setlocale: LC_TIME: 无法改变区域选项 (zh_CN.UTF-8)
    https://www.cnblogs.com/walkersss/p/17442533.html使用ssh远程登陆centos,出现如下告警信息:bash:警告:setlocale:LC_TIME:无法改变区域选项(zh_CN.UTF-8)原因分析:系统已经设置了默认地区_语言.字符集为zh_CN.UTF-8,但是在系统中没有定义对应的locale文件,所以只需要手动生......