java虚拟机(内存模型)-----------记录深入java虚拟机章节重点

整体分布:
在这里插入图片描述

虚拟机栈(本地方法栈和他一样滴)

线程独属滴,当执行java代码时,A线程会拥有一个自己的栈,A线程执行方法时,就会将方法做为栈帧压入A栈中,其中栈帧中,存放一个局部变量表,局部变量表中,存放了该方法中定义的局部变量(int,long等直接存放,引用类型存地址指针,指令的话就存字节码指令的地址),编译期间就确定大小。(肯定的啊,又不会存对象,大小自然是固定的),局部变量表中32位一个空间,如图所示,指令一步步被压入了栈中。栈深度过大,则抛出stackoverflow。(现在的虚拟机都支持动态扩展了,支持动态扩展的话就抛出OutofMemory异常),通过-XX:Xss:10m和-XX:XsX:10m设置每一个栈的大小

在这里插入图片描述

就是个存储对象的地方。线程共享的。
在这里插入图片描述执行到上图的时候就会建堆。
模拟异常,并打印堆栈:
虚拟机参数:

-Xms4m -Xmx4m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=G:\GIT\base

执行代码:

public class HeapOOM {
 public class A{
  private long a = 0L;
  private int b = 0;
 }
 
 public static void main(String[] args) {
  ArrayList<HeapOOM> a = new ArrayList<HeapOOM>();
  while(true){
   a.add(new HeapOOM());
  }
 }
}

执行结果:
在这里插入图片描述
下载Eclipse memory analyze 工具,打开该dump的文件。如图:分析是内存泄漏还是内存溢出。
在这里插入图片描述
请注意,当碰到oom异常时,请先打印堆栈,然后调整参数,因为堆栈(两种)+方法区+堆差不多等于虚拟机容量。如果因为线程过多(占用堆栈大小),则应减少堆栈大小,或者减小堆。

方法区

存储被虚拟机加载的类信息,常量,静态变量。编译器编译后的代码等数据。(Hotspot刚开始gc收集会僭越过来收集,并被gc当做永久代收集,主要收集常量池和类型卸载,虽然收集的令人不满意,但是还是要收集,后来逐步采用Native Memory实现,jdk7开始,),内存不足则抛出OutofMemory异常,常见的permGen space就是方法区溢出。可通过-XX:permSize和-XX:maxPermSize
模拟时可通过代理动态生成类

运行时常量池

是方法区的一部分,存放类信息中的常量,类加载完毕后,就会放入该地方。运行期间,新的常量有可能会被放入池中,String的intern()方法会用到该地方。一样会抛出OutofMemory异常,-XX:permSize和-XX:maxPermSize

顺便解释一下String的intern()方法。intern方法就是,常量池中存在的话,就返回常量池中的字符串,常量池中不存在的话,就将字符串放在常量池,然后返回常量池地址。
此处用 https://www.cnblogs.com/xys1228/p/6035480.html 该博客的例子。

博客举的例子:

// 1
String str1 = new StringBuilder("ja").append("va").toString();
System.out.println(str1.intern() == str1);

// 2
String str2 = new StringBuffer("编").append("程").toString();
System.out.println(str2.intern() == str2);

// 3
String str3 = new StringBuffer("编").append("程").toString();
System.out.println(str3.intern() == str3);

// 使用 JDK6 进行编译运行(字符串常量池就存字符串):
false, false, false
// 使用 JDK7 进行编译运行(字符串常量池存堆地址):
false, true, false

直接内存

Nio基于Native函数库直接分配堆外内存。并不属于虚拟机的内存,通过java堆中的DirectBuffer对象作为那块地方引用进行操作。避免在java堆和Native堆中来回复制数据。可通过-XX:MaxDirectMemberSize:10m设置大小,不设置就和Xmx大小相同。

java对象分配过程

java分配内存时。会给每个线程在堆中分配一个本地线程分配缓冲(TLAB),防止出现这种情况:A a = new A();另一个线程B b = new B(),堆中分配完A的内存:c地址,还没来得及将a指向c地址,b就以为那块内存是空闲的,就开始适用那片内存。TLAB就是防止那种情况的出现。-XX:+/-UseTLAB来决定。

分配完内存,就要初始化为0值。然后初始化对象头。然后初始化值。

对象在内存中存储=对象头+实例数据+对齐填充

对象头=自身运行时数据(长度与虚拟机位数相同)+对象指向类元数据的指针。

32位情况下:自身运行时数据=25的哈希码+4对象分代年龄+1个固定位+2锁标志位
在这里插入图片描述

实例数据就是java对象中的各种字段及继承的各种字段。

在这里插入图片描述
上图为java的两者引用方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值