当前位置:网站首页>对象实例化之后一定会存放在堆内存中?
对象实例化之后一定会存放在堆内存中?
2022-08-04 17:18:00 【兄dei!】
目录
前言
最近在学JVM,学到内存结构的时候产生一个疑问。
疑问主要来自这样一句话:"所有的对象实例以及数组都应当在堆上分配——《Java虚拟机规范》"
乍一看好像没什么,仔细一想,前面说的栈内存是线程私有,也就是说在方法体里面,局部变量是线程私有的也就是存放于栈内存里面。但是如果在方法体里面创建对象,让对象实例化呢?
于是动手做起了小实验
期间用到了几个命令:
jps -->查看运行中的线程id
jmap -heap 线程id -->查看堆内存情况
实验得出的结论
1.实例化后存在堆中的情况
作为共享变量,需要被其它线程使用,所以是在堆内存之中。
作为局部变量但是有发生逃逸的情况(return 出去了或者是作为参数传进来)
作为局部变量但是占用内存空间较大(超出栈内存)
2.实例化对象后存在栈中的情况
作为局部变量并且不发生逃逸现象,并且占用内存空间不能超出栈内存限制。
所以说,对象实例化之后不一定会存放于堆内存之中,也可能被优化存放于栈内存之中。
逃逸分析的概念
先以官方的形式来说下什么是逃逸分析。逃逸分析就是:一种确定指针动态范围的静态分析,它可以分析在程序的哪些地方可以访问到指针。
在JVM的即时编译语境下,逃逸分析将判断新建的对象是否逃逸。即时编译判断对象是否逃逸的依据:一种是对象是否被存入堆中(静态字段或者堆中对象的实例字段),另一种就是对象是否被传入未知代码。
逃逸的情况下对象实例化之后存放
经过测试,发现如果后续没有其它线程参与使用共享变量的情况下,JVM总是不会对堆内存有想法。
测试了对象实例化之后return逃逸出当前方法,如果没有其它线程使用的话,堆内存仍然没有什么变化。
只有当对象实例化之后,被其它线程使用的情况下它才会跑到堆内存里面去。
// TODO: 2022/8/4 测试逃逸的情况下对象实例化的存放(return list)
public static List testHeap3() throws InterruptedException {
System.out.println("new了对象之前...");//jps 命令查看线程id
Thread.sleep(20000);
List<String>list=new ArrayList<>(10);
String a="hello ";
for (int i = 0; i < 10; i++) {
list.add(a);
a=a + a;
}
System.out.println("准备return了...");//jmap -heap id
Thread.sleep(20000);
return list;
}
main方法:
private static List<String>list;
public static void main(String[] args) throws InterruptedException {
list=testHeap3();
new Thread(()->{
try {
System.out.println("准备使用共享变量..");
Thread.sleep(10000);
for (String s : list) {
System.out.println(s);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
1.没有其它线程参与之前
2.其它线程参与之后
测试不逃逸的情况下对象实例化的存放(占用内存大)
使用比较大的空间测试
结论: 不逃逸的情况下,虽然是线程私有,但是由于实例化对象的时候发现栈内存不足以存放该对象,所以还是分配到堆中创建,因为10MB早已超出栈内存
public static void testHeap1() throws InterruptedException {
System.out.println("new了对象之前...");//jps 命令查看线程id
Thread.sleep(20000);
byte[] bys=new byte[1024 * 1024 *10];//10MB 堆中创建
System.out.println("new了对象之后...");//jmap -heap id
Thread.sleep(20000);
}
1.
2.
测试不逃逸的情况下对象实例化的存放(占用内存小)
如果new的对象占用空间比较小呢?-->会被JIT即时编译器优化在栈中运行
public static void testHeap1() throws InterruptedException {
System.out.println("new了对象之前...");//jps 命令查看线程id
Thread.sleep(20000);
byte[] bys=new byte[256]; //256b -->优化到栈中创建
System.out.println("new了对象之后...");//jmap -heap id
Thread.sleep(20000);
}
1.
2.
测试对象从栈内存转移到堆内存
如果一开始是在栈里面,有没有可能后期转移到堆中呢?
猜想: 如果占用内存变大,是会转移过去的
测试代码:
// TODO: 2022/8/4 测试堆内存溢出的现象
public static void testHeap2() throws InterruptedException {
System.out.println("new了对象之前...");//jps 命令查看线程id
Thread.sleep(20000);
List<String>list=new ArrayList<>();
System.out.println("new了对象之后...");//jmap -heap id
Thread.sleep(20000);
String a="hello ";
int i=0;
try {
while (true){
list.add(a);
a=a + a;
i++;
if (i==20){
System.out.println("达到20次了...");//todo 此时再查发现 以及从栈内存转移到堆内存中了
Thread.sleep(20000);
}
}
}catch (OutOfMemoryError e){
e.printStackTrace();
System.out.println(i);
}
}
1.一开始对象实例化之前,栈内存占用情况
2.对象实例化之后,堆内存占用情况
可以看到堆内存占用情况没有改变,说明此时实例化是在栈内存中。
3.当循环了20次之后,占用空间已经不小了,此时已经被转移到堆内存
4.由于是死循环,最后抛出异常是堆内存溢出
边栏推荐
- arm交叉编译
- init和destory方法
- 基于clipboard.js对复制组件的封装
- Clearance sword refers to Offer——The sword refers to Offer II 010. and the sub-array of k
- 【LeetCode Daily Question】——374. Guess the size of the number
- mysql学习笔记——利用动态SQL和Session变量实现一个公式或者计算器
- 小程序+自定义插件的混合模式
- mysqlbinlog 超过500g自动删除,保留7个,求大深给个版本
- Json的FastJson与Jackson
- 通关剑指 Offer——剑指 Offer II 010. 和为 k 的子数组
猜你喜欢
【LeetCode每日一题】——540.有序数组中的单一元素
【LeetCode每日一题】——374.猜数字大小
并发编程原理学习-reentrantlock源码分析
谷歌开发者社区推荐:《Jetpack Compose 从入门到实战》新书上架,带你踏上 Compose 开发之旅~
知乎高赞:拼多多和国家电网,选哪个?
御神楽的学习记录之基于FPGA的AHT10温湿度数据采集
罗振宇折戟创业板/ B站回应HR称用户是Loser/ 腾讯罗技年内合推云游戏掌机...今日更多新鲜事在此...
西西成语接龙小助手
《机器学习的随机矩阵方法》
Learning and Exploration-Introducing Baidu Statistics to the Website
随机推荐
Qt自动补全之QCompleter使用
response的contentType 几种类型
44. 通配符匹配 ●●● & HJ71 字符串通配符 ●●
自定义组件,并在组件中注入自定义组件实现多种场景的下的组件切换
在VMD上可视化hdf5格式的分子轨迹文件
.NET云原生应用发展论坛--8月7日邀你一起云上探索
学习探索-给字体设置前景色
设置表头颜色
泰坦尼克号沉船数据之美——起于悲剧,止于浪漫
Boost library study notes (1) Installation and configuration
乐享购(分享购)的模式:优势、亮点、收益
谷粒商城笔记
理财产品买入后份额是固定不变的吗?
适配器模式
Nacos集群搭建
【商家联盟】云平台—异业联盟,打造线上线下商业相结合的系统
MySQL学习笔记-4.数据更新时的性能问题
win11如何退出安全模式
为什么买域名必须实名认证?这样做什么原因?
西西成语接龙小助手