解析java内存区域

为什么要了解虚拟机

  • JVM不单单只支持Java语言,也支持其他语言(Scala、Kotlin、Groovy等等)
  • 写出更好,更健壮的程序 (区块链1.0:比特币,区块链2.0:以太坊,以太坊中提供了EVM的虚拟机,它的实现和JVM类似,基于栈、生成脚本编译成字节码来执行)
  • 提高java应用的性能,更好更快速的排除解决相关问题

Java SE体系架构

Java_SE体系架构

虚拟机的发展史

1) Hotspot VM 之前输入SUN公司后面被甲骨文公司收购,Hotspot什么意思:热点代码探测技术,及时编译器(发现最有价值的代码,如果代码用得非常多,就会把这些代码编译成本地代码)。
2) JRockit VM 号称”世界上最快的Java虚拟机” ,最后也被甲骨文公司收购
3) J9 VM 是属于IBM的 目前华为在使用
4) Dalvik VM google的产品

JVM 和 DVM 的区别

  • 首要差别
    Dalvik: 基于寄存器,编译和运行都会更快些
    JVM: 基于栈, 编译和运行都会慢些
  • 字节码的区别
    Dalvik: 执行.dex格式的字节码,是对.class文件进行压缩后产生的,文件变小
    JVM: 执行.class格式的字节码
  • 运行环境的区别
    Dalvik : 一个应用启动都运行一个单独的虚拟机运行在一个单独的进程中
    JVM: 只能运行一个实例, 也就是所有应用都运行在同一个JVM中

未来的Java技术

  • 模块化:使用得最多OSGI,应用层面就是微服务,互联网的发展方向
  • 混合语言:多个语言都可以运行在JVM中
  • 多核并行:CPU从高频次转变为多核心,多核时代。JDK1.7引入了Fork/Join,JDK1.8提出lambda表达式(函数式编程天生适合并行运行)
  • 丰富语法:JDK5提出自动装箱、泛型(并发编程讲到)、动态注解等语法。JDK7二进制原生支持。try-catch-finally 至try-with-resource
  • 64位:虽然同样的程序64位内存消耗比32位要多一点,但是支持内存大,所以虚拟机都会完全过渡到64位。
  • 更强的垃圾回收:JDK11 –ZGC(TB级别内存回收)):有色指针、加载屏障

运行时数据区域

  • 定义:Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域
  • 类型:程序计数器、虚拟机栈、本地方法栈、Java堆、方法区(运行时常量池)、直接内存

程序的计数器 :指向当前线程正在执行的字节码指令的地址(行号)

  • 为什么需要程序计数器
    1)Java是多线程的,意味着线程需要切换
    2)确保多线程情况下的程序正常执行(线程切换后能在正确的位置继续执行)

  • 栈: 栈是一种数据结构,入口和出口只有一个,遵循FILO原则(first in last out :先进后出)
  • 为什么JVM要使用栈: 非常符合JAVA中方法间的调用(方法A调用方法B: A方法先开始最后结束,B方法最后开始最先结束)
  • **方法递归的问题:**因为一个方法执行的时候放入虚拟机栈中,遵循先进后出的原则,如果方法又递归没有中断递归的判断的条件,最终的结果就是栈溢出错误,因为一直向虚拟机栈中添加方法没有出栈的操作,栈的大小是受限制的,迟早会装不下就溢出了。

虚拟机栈

每个线程私有的,线程在运行时,在执行每个方法的时候都会打包成一个栈帧,存储了局部变量表,操作数栈,动态链接,方法出口等信息,然后放入栈。每个时刻正在执行的当前方法就是虚拟机栈顶的栈桢。方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程。
栈的大小缺省为1M,可用参数 –Xss调整大小,例如-Xss256k

  • **局部变量表:**顾名思义就是局部变量的表,用于存放我们的局部变量的。首先它是一个32位的长度,主要存放我们的Java的八大基础数据类型,一般32位就可以存放下,如果是64位的就使用高低位占用两个也可以存放下,如果是局部的一些对象,比如我们的Object对象,我们只需要存放它的一个引用地址即可。
  • **操作数栈:**存放我们方法执行的操作数的,它就是一个栈,先进后出的栈结构,操作数栈,就是用来操作的,操作的的元素可以是任意的java数据类型,所以我们知道一个方法刚刚开始的时候,这个方法的操作数栈就是空的,操作数栈运行方法是会一直运行入栈/出栈的操作
  • **动态连接:**Java语言特性多态(需要类加载、运行时才能确定具体的方法),动态特性(Groovy、JS、动态代理)
  • **返回地址:**正常返回(调用程序计数器中的地址作为返回)、异常的话(通过异常处理器表<非栈帧中的>来确定)

本地方法栈

本地方法栈保存的是native方法的信息

  • 当一个JVM创建的线程调用native方法后,JVM不再为其在虚拟机栈中创建栈帧,JVM只是简单地动态链接并直接调用native方法
  • 虚拟机规范无强制规定,各版本虚拟机本地方法栈自由实现,HotSpot直接把本地方法栈和虚拟机栈合二为一

线程共享的区域

  • **方法区:**用于存储已经被虚拟机加载的类信息(类信息,常量(final),静态变量(static),即时编译期编译后的代码)可用以下参数调整方法区大小:
    jdk1.7及以前:-XX:PermSize;-XX:MaxPermSize;
    jdk1.8以后:-XX:MetaspaceSize; -XX:MaxMetaspaceSize
    jdk1.8以后大小就只受本机总内存的限制
    如:-XX:MaxMetaspaceSize=3M
  • Java堆: 保存对象实例(几乎所有) ,数组。
    java堆的大小参数设置:
    -Xmx 堆区内存可被分配的最大上限
    -Xms 堆区内存初始内存分配的大小

直接内存

image.png

  • **直接内存:**不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域;
    1)如果使用了NIO(同步非阻塞IO,面向缓冲的),这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作;
    2)这块内存不受java堆大小限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常;

站在线程的角度来看JVM的运行数据区

线程角度

深入辨析堆和栈

  • 功能:
    1)以栈帧的方式存储方法调用的过程,并存储方法调用过程中基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以及对象的引用变量,其内存分配在栈上,变量出了作用域就会自动释放;
    2)而堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中;
  • 线程独享还是共享:
    1)栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。
    2)堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
  • 空间大小:
    1)栈的内存要远远小于堆内存,栈的深度是有限制的,可能发生StackOverFlowError问题。

虚拟机中的对象

设置步骤主要是设置对象头等信息

  • CAS(Compare and swap)比较和交换
    CAS操作需要输入两个数值,一个旧值A(期望操作前的值)和一个新值B,在操作期间会先比较旧值有没有变化,如果没有变化,才交换成新值,否则不进行交换
    内存分配采用CAS,提高安全性

对象的内存布局

对象的内存分布

对象的访问定位

句柄池访问:如果一个对象没了,只需要修改句柄池,垃圾回收就很快速
直接指针访问:不通过句柄池,访问速度很快

虚拟机优化技术——逃逸分析

  • 逃逸分析
    逃逸分析是目前JVM中比较前沿的优化技术,它不是直接的优化手段而是为其他优化手段提供依据的分析技术,逃逸分析的基本行为就是分析对象动态作用域。

  • 几乎所有对象都在堆上分配,有特列情况对象是分配在栈上的。(一个对象如果没有逃逸出方法就可能分配在栈上)
    默认情况是开启了逃逸分析的,这时Dinner是在栈中分配的,而不是堆上,这个就是特殊情况
    在运行参数设置的VM options中设置:-XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:-UseTLAB
    关闭逃逸分析得到结果:
    这时dinner对象就非配在堆上,因为太多所以的频繁垃圾回收,所以耗时长

  • 牵涉到的JVM参数: + 是打开 - 是关闭
    -XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)
    -XX:+EliminateAllocations:标量替换(默认打开)
    -XX:+UseTLAB 本地线程分配缓冲(默认打开)

方法在栈中执行的分析图

方法在栈中执行的分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

积跬步以致千里_ylc

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

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

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

打赏作者

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

抵扣说明:

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

余额充值