JVM内存管理--笔记

Java代码是如何运行起来的?

Java文件->编译器->字节码->JVM->机器码
Java程序,先经过Javac编译成.class文件,如何JVM将其加载到‘元数据’区,执行引擎通过混合模式执行这些字节码,执行时,会翻译成操作系统相关的函数,JVM作为.class文件的黑盒存在,输入字节码,调用操作系统函数

JVM内存管理

JVM内存区域划分(运行时数据区)
在这里插入图片描述

  • JVM堆中的数据是共享的,这是占用内存最大的一块区域
  • 执行引擎:可以执行字节码的模块
    • 执行引擎在线程切换时如何恢复–依靠程序计数器

程序计数器

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

  • Java虚拟机多线程是通过线程轮流切换,分配处理器执行时间的方式(时间片轮转)来实现的,在某一个时刻,一个处理器只能执行一条线程中的指令,因此,为了不同线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,且各线程的程序计数器之间互不影响,独立存储,因此,程序计数器属于线程私有的内存

  • 若线程执行的是Java方法,则程序计数器中记录的是正在执行的虚拟机字节码指令的地址

  • 若线程执行的是native方法,则程序计数器的值为空(undefined)

    • native方法是java通过JNI直接调用本地C/C++库,可以近似的认为native方法相当于C/C++暴露给java的一个接口,java通过调用这个接口从而调用到C/C++方法。由于该方法是通过C/C++而不是java进行实现。那么自然无法产生相应的字节码,并且C/C++执行时的内存分配是由自己语言决定的,而不是由JVM决定的。

在这里插入图片描述

  • .程序计数器,是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError的区域。

Java虚拟机栈

  • Java内存管理和线程息息相关,每个Java方法调用时,都会创建一个栈帧,入栈,完成相应调用后,出栈,所有栈帧出栈后,线程结束,因此,Java虚拟机栈也是线程私有的,生命周期于线程相同
    注意:栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构。它是虚拟机运行时数据区中的虚拟机栈的栈元素。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机里面从入栈到出栈的过程。)

栈帧包含的四个区域:
在这里插入图片描述
在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧相关联的方法称为当前方法

局部变量表

  • 局部变量表一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量

  • 在Java编译为Class文件时,就已经确定了该方法所需要分配的局部变量表的最大容量,在方法运行期间不会改变局部变量的大小

  • 该内存区域有两类异常状况

    • 若线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
    • 若Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存,就会排除OutOfMemoryError异常(HotSpot虚拟机的栈容量不可动态拓展,只有线程申请栈空间成功就不会有OOM异常,但如果申请时失败,仍会抛出OOM异常)
变量槽
  • 局部变量表以变量槽为最小单位
  • 比如,64为长度的long和double类型的数据占用两个变量槽,其余的数据类型占用一个变量槽
  • 一个变量槽有多大(占用多少比特),这取决于具体的虚拟机实现

本地方法栈

  • 本地方法栈和虚拟机栈的结构和作用非常相似,区别:虚拟机栈为虚拟机执行Java方法(字节码服务),本地方法栈为虚拟机使用到的native方法服务
  • 本地方法栈也会在栈深度溢出或者栈拓展失败时抛出StackOverflowError异常和OutOfMemoryError异常

Java堆

  • Java堆是虚拟机管理内存中最大的一块,所有线程共享,在虚拟机启动时创建
  • 用于存放对象实例
  • Java堆是垃圾收集器管理的内存区域,也称GC堆
  • 从分配内存的角度看,所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),以提升对象分配时的效率
  • Java堆可以处于物理上不连续的内存空间中,但在逻辑上应被视为连续的
  • Java堆可以被实现成固定大小,也可以是可拓展的
  • 当Java堆中没有内存完成实例分配,并且堆也无法再拓展时,Java虚拟机会抛出OutOfMemoryError异常
  • Java堆中会划分**新生代 ( Young )、老年代 ( Old )**区域

方法区

  • 方法区是各个线程共享的内存区域
  • 用于储存已被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存等数据
  • .也称静态区,包含所有的class和static变量。方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
  • 方法区不需要连续的内存,可以选择固定大小或者可拓展,可以选择不是先垃圾收集(垃圾收集行为在该区域比较少出现,该区域的内存回收目标主要是针对常量池的回收和堆类型的卸载)
  • 若方法区无法满足新的内存分配需求时,将抛出OutOfMermoryError异常

运行时常量池

  • 运行时常量池是方法区的一部分
  • Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到常量池中
  • 具备动态性
    • Java语言不要求常量一定只有编译器才能产生,也就是说,并非预置入class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中
  • 由于运行时常量池是方法区的一部分,因此,常量池再申请到内存时会抛出OutOfMemoryError异常

直接内存

  • 直接内存不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域,但这部分区域被频繁使用过,并且也可能导致OutOfMemoryError异常
  • 在jdk1.4中新加入了NIO(New Input/Output)类,引入了基于通道于缓冲区的I/O方式,可以使用native函数库直接分配堆外内存,如何通过一个存储在Java堆里的DirectByteBuffer对象作为这块内存的引用进行操作,这样在一些场景中可以提高性能
  • 本机直接内存的分配不会收到Java堆大小的影响,但还是会收到本机总内存大小以及处理器寻址空间的限制
  • 直接内存的读写操作比普通Buffer快,但它的创建、销毁比普通Buffer慢
  • 直接内存使用于需要大内存空间且频繁访问的场合,不适用于频繁申请释放内存的场合
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值