JVM—内存结构

本文详细介绍了JVM的内存结构,包括程序计数器、虚拟机栈、本地方法栈、堆和方法区。讨论了线程安全、内存溢出及字符串常量池等概念,并举例说明了内存溢出的产生原因。同时,提到了不同操作系统中栈内存的默认配置以及如何手动调整。
摘要由CSDN通过智能技术生成

JVM的内存结构

内存结构组成部分:

  1. 程序计数器 PC Register
  2. 虚拟机栈 JVM Stacks
  3. 本地方法栈 Native Method Stacks
  4. 堆 Heap
  5. 方法区 Method Area

一个程序到cpu执行的过程:

java程序-----(编译)----->.class文件[二进制字节码]-----(解释器解释)----->机器码-----(可执行)----->cpu执行


程序计数器

  • 作用: 记住下一条将要执行的指令的地址;

  • 特点:

    • 程序计数器是线程私有的,每一个线程都有自己独立的程序计数器;
    • 程序计数器不存在内存溢出;

虚拟机栈(线程栈)

  • 作用: 线程运行所需要的内存空间;

  • 每个栈由多个栈帧(Frame)组成,对应着每次调用放法时所占用的内存;

    栈帧:

    • 每个方法运行时所需要的内存(方法参数,局部变量,返回地址);
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的方法;(根据栈的特性可知,该活动栈帧就是虚拟机栈顶部的栈帧)

垃圾回收是否涉及栈内存?

不涉及,栈帧运行结束后就自动弹出,虚拟机栈内存空间会自动释放不需要垃圾回收操作;

栈内存分配越大越好吗?

否; 栈内存分配得越大,一定程度上会减少同时运行的线程的数量,每个线程栈空间变大,但是总的内存空间不变,线程数量减少.

linux,macOs,Oracle Solaris系统和默认分配栈内存为1024KB(1M);

Windows根据虚拟机内存动态默认分配;

手动设置栈内存大小的方法: -Xss256k 设置为256k

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

完全属于某个方法私有的变量是安全的(该变量没有逃离方法的作用范围);

通过参数传入,或者作为返回值返回的引用对象需要考虑线程安全问题,因为除了该方法外的其他方法/线程可以访问到该引用对象,就有可能会同时对该变量进行操作.

**栈内存溢出: **

异常报错信息:

  • java.lang.StackOverFlowError

产生原因:

  • 栈帧过多导致栈内存溢出
    • 例子: 没有合理的递归结束条件,递归无限的调用自己;
  • 栈帧过大(比较少)

本地方法栈

不是由java代码编写的方法 (由C或者C++实现的native方法) 运行时所需要的栈空间就是本地方法栈.

Object中很多方法就是本地方法:

clone()

notify()

hashCode()

notifyAll()

wait()


堆的定义:

  • 通过new关键字创建的对象都会使用堆内存

堆的特点:

  • 他是所有线程共享的,堆中对象都需要考虑线程安全问题
  • 由垃圾回收机制

**堆内存溢出: **

异常报错信息:

  • java.lang.OutOfMemoryError: Java heap space

设置堆内存大小(一般默认4G):

  • -Xmx8m 更改为8m

方法区

定义:

  • 存储类的结构的信息的内存区域, 如: 运行时常量池,类的成员变量,方法数据,成员方法和构造器,类加载器等;
  • 运行时常量池: [StringTable详解]( (1条消息) StringTable详解_百里屠苏的博客-CSDN博客_stringtable )
    • jdk1.6以前运行时常量池包括 字符串常量池StringTable 和 其他组成部分;
    • jdk1.7以后,字符串常量池StringTable不属于运行时常量池,StringTable单独放在堆中.

特点:

  • 所有线程共享的区域
  • 在虚拟机启动时开始运行

hotspot jdk1.6以前,方法区使用的是堆内存空间,叫做永久代;

jdk1.8后,方法区移到了系统内存中,不再放在JVM管理的内存中,叫做元空间,字符串常量池依然放在堆内存.

方法区内存溢出

设置方法区内存大小:

  • 1.8以前的永久代: -XX:MaxPermSize=8m 设置最大内存为8m
  • 1.8以后的元空间: -XX:MaxMetaspaceSize=8m 设置最大内存为8m

jdk1.8的元空间使用的时系统内存,系统内存较大, 所一不容易导致内存溢出.但是溢出的理论存在,将元空间的最大空间设置小一些的话可以造成溢出.

运行时常量池

常量池:

  • 就是一张.class文件的信息表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型,字面量的信息;

运行时常量池:

  • 当一个.class文件被加载时,他的常量池信息就会放入到运行时常量池,并且把里面的符号地址转换为真实地址;
  • class文件的信息被加载到运行时常量池的时候,文件中定义的对象(字符串对象等等)都只是一些符号,不是真正的对象.当指令真正执行到的时候才会转换成真正的对象.指令没执行到这些符号就不会变成对象.(可以理解为懒加载)

  • 常量池中的字符串符号转换为真正的对象后,会在**StringTable(串池)**中找有没有这个字符串对象,如果发现串池中有,那么就直接使用串池中的对象,如果没有,就将该对象添加到串池中.

当指令真正执行到的时候才会转换成真正的对象.指令没执行到这些符号就不会变成对象.(可以理解为懒加载)**

  • 常量池中的字符串符号转换为真正的对象后,会在**StringTable(串池)**中找有没有这个字符串对象,如果发现串池中有,那么就直接使用串池中的对象,如果没有,就将该对象添加到串池中.
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莽晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值