百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Jvm专讲之内存结构

toyiye 2024-05-19 19:36 11 浏览 0 评论

Jvm内存模型

1.JDK体系架构

从上面JDK体系架构图可以看出来:

  • JDK包含了java的常用工具开发包和jre
  • jre是java运行时环境,包括了jvm和Java核心类库
  • jvm负责把字节码文件解释为操作系统可以识别的机器码

2.Java语言的跨平台性

我们的java文件经过编译形成class文件后,为什么可以放在装有jdk的操作系统上就可以运行呢?这是因为针对不同的操作系统有不同的jvm实现,而jvm负责把我们编译好的字节码文件翻译为操作系统可以识别的机器码并运行。

3.Jvm的内存模型

声明一个Math类

java复制代码public class Math {

    public static final int initData = 10;

    public static User user = new User();

    public static String s = new String();

    public int compute(){
        int a = 1;
        int b = 2;
        int c = (a + b) *10;
        return c;
    }

    public static void main(String[] args) {
        Math math = new Math();

        math.compute();

        System.out.println(s.getClass().getClassLoader());
    }
}

Jvm的内存模型如上图,主要分为方法区,堆空间,虚拟机栈,本地方法栈,程序计数器,接下来为大家详细描述每一块区域:

3.1 虚拟机栈

栈空间是我们线程私有的,当我们每次创建一个新的线程的时候,jvm都会为它分配一块私有的栈内存,当线程执行方法的时候,每执行一个方法会形成一个栈帧,最先进入的方法被压到栈底,遵循先进后出的原则。

在栈帧中都有什么呢?它主要存放的是局部变量表,操作数栈,动态链接,方法出口,我们先把math.class文件生成反汇编文件来了解这些内存区域

javascript复制代码javap -c Math.class > Math.txt

经过这个指令,我们的Math类生成的反汇编文件compute方法内容如下:

主要看我们的compute方法这里的指令,iconst_1是把我们的1放在操作数栈空间中,istore_1 是把1这个数存入局部变量表索引1的这个位置,也就是a=1,iconst_2是把2压入操作数栈,istore_2把2存入局部变量表索引2的位置,也就是b=2,iload_1是从局部变量表索引1位置取出操作数,iload_2 从局部变量索引2位置取出操作数,iadd就是把两个数相加,bipush 10 将一个8位带符号整数10压入操作数栈,imul是进行一次乘法运算,istore_3是将i相乘的结果存入局部变量表索引3的位置,

iload_3 从局部变量索引3的位置中中装载取值,ireturn将结果进行返回

iconst_1 将int类型常量1压入操作数栈

istore_1 将int类型值存入局部变量1

iconst_2 将int类型常量2压入操作数栈

iload_1 从局部变量1中装载int类型值

iload_2 从局部变量2中装载int类型值

iadd 执行int类型的加法

bipush 将一个8位带符号整数压入栈

imul 执行int类型的乘法

istore_3 将int类型值存入局部变量索引3位置

ireturn 从方法中返回int类型的数据

3.2 本地方法栈

本地方法栈和虚拟机栈一样,是当线程执行本地方法的时候虚拟机开辟的一片空间,只不过这里的方法是本地方法,方法实现是通过c或者c++实现的。

3.3 程序计数器

程序计数器也是线程私有的,主要目的是当线程发生上下文切换的时候,记录一下我们代码的运行位置,字节码解释器工作时,通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

3.4 方法区

方法区在JDK1.8和JDK1.7的概念是不同的,在JDK1.7中我们的方法区被称作永久代,其内存区域是由JVM提供的,而JDK1.8中被称作元空间,它使用的是我们物理机的直接内存,在方法区主要存储的是我们的常量,静态变量,类信息

3.5 堆

堆在虚拟机启动时创建,是Java 虚拟机所管理的内存中最大的一块,主要用于存放对象实例,几乎所有的对象实例都在这里分配内存,注意Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做GC堆,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常。

3.6 常量池

3.6.1 Class常量池和运行时常量池

Class常量池可以理解为是Class文件中的资源仓库。 Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table) ,用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic References)

字面量:就是等号右边的数字、字符串,如:int a=1 这里的1为就是字面量

符号引用:符号引用是编译原理中的概念,是相对于直接引用来说的,主要包括了以下三类常量:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符
  • 比如a=1中a就是一个符号引用,比如一个类的全路径com.lx.Math,包含类中的方法比如main、test等,()是一种UTF8格式的描述符,这些都是符号引用。
  • 这些常量池现在是静态信息,只有到运行时被加载到内存后,这些符号才有对应的内存地址信息,这些常量池一旦被装入内存就变成运行时常量池,对应的符号引用在程序加载或运行时会被转变为被加载到内存区域的代码的直接引用,也就是我们说的动态链接了。例如,main()这个符号引用在运行时就会被转变为main()方法具体代码在内存中的地址,主要通过对象头里的类型指针去转换直接引用。

3.6.2 字符串常量池

3.6.2.1 字符串常量池的设计思想

  1. 字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能
  2. JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
  3. 为字符串开辟一个字符串常量池,类似于缓存区
  4. 创建字符串常量时,首先查询字符串常量池是否存在该字符串
  5. 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中

3.6.2.2 字符串常量池位置

Jdk1.6及之前: 有永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池

Jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里

Jdk1.8及之后: 无永久代,运行时常量池在元空间,字符串常量池里依然在堆里

如何证明,jdk1.6之后我们的字符串常量池是在堆中的,请看一下程序和结果

arduino复制代码//-Xms6M -Xmx6M 
public class StringConstantPoolOOM {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000000; i++) {
            String str = String.valueOf(i).intern();
            list.add(str);
        }
    }
}

运行结果:堆空间不足

接下来我们通过案例一起更进一步分析一下字符串常量池

三种字符串操作:

ini复制代码String s = "luoxue";  // s指向常量池中的引用

这种方式创建的字符串对象,只会在常量池中,因为有"luoxue"这个字面量,创建对象s的时候,JVM会先去常量池中通过 equals(key) 方法,判断是否有相同的对象,如果有,则直接返回该对象在常量池中的引用,如果没有,则会在常量池中创建一个新对象,再返回引用。

javascript复制代码String s1 = new String("luoxue");  // s1指向内存中的对象引用

这种方式会保证字符串常量池和堆中都有这个对象,没有就创建,最后返回堆内存中的对象引用。解析:因为有"qianyue"这个字面量,所以会先检查字符串常量池中是否存在字符串"qianyue",不存在,先在字符串常量池里创建一个字符串对象;再去内存中创建一个字符串对象"qianyue",存在的话,就直接去堆内存中创建一个字符串对象"qianyue",最后,将内存中的引用返回。

ini复制代码        String s1 = new String("luoxue"); //s1->指向堆中的对象
        
        String s2 = s1.intern();//s2->常量池中的对象

        System.out.println(s1);//luoxue
        System.out.println(s2);//luoxue
        System.out.println(s1==s2);//false
        

String中的intern方法是一个 native 的方法,当调用 intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(oject)方法确定),则返回池中的字符串。否则,将intern返回的引用指向当前字符串 s1

ini复制代码        //堆中创建了一个字符串hello,但是没有在字符串常量池中创建,s1指向堆中hello对象的地址
        String s1 = new String("he")+new String("llo");
        //发现常量池没有,直接返回堆中hello的地址
        String s2 = s1.intern();

        System.out.println(s1);//hello
        System.out.println(s2);//hello
        System.out.println(s1==s2);//true

4.JVM内存参数设置

Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里):

ini复制代码java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar microservice‐eurek
a‐server.jar

关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N

  • -XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
  • -XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发 full gc进行类型卸载, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。这个跟早期jdk版本的-XX:PermSize参数意思不一样,- XX:PermSize代表永久代的初始容量。

由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大, 对于8G物理内存的机器来说,一般会将这两个值都设置为256M。



相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码