当前位置:网站首页>对象实例化之后一定会存放在堆内存中?
对象实例化之后一定会存放在堆内存中?
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.由于是死循环,最后抛出异常是堆内存溢出

边栏推荐
猜你喜欢

Clearance sword refers to Offer——The sword refers to Offer II 010. and the sub-array of k

【LeetCode每日一题】——540.有序数组中的单一元素

88.(cesium之家)cesium聚合图

乐享购(分享购)的模式:优势、亮点、收益

罗振宇折戟创业板/ B站回应HR称用户是Loser/ 腾讯罗技年内合推云游戏掌机...今日更多新鲜事在此...

不需要服务器,教你仅用30行代码搞定实时健康码识别

Digital-intelligent supply chain management system for chemical manufacturing industry: build a smart supply system and empower enterprises to improve production efficiency

知乎高赞:拼多多和国家电网,选哪个?

跨域传递数据(iframe)

学习探索-网站中引入百度统计
随机推荐
域名哪家便宜?怎么买便宜域名?
《机器学习的随机矩阵方法》
el-date-picker 设置时间范围
SAP 电商云 Spartacus UI 页面布局的设计原理
学习探索-给字体设置前景色
R语言使用cov函数计算矩阵或者dataframe数据变量之间的协方差、cor函数计算相关性、cor函数通过method参数指定相关性、相关性计算方法Pearson,Spearman, Kendall
Nacos集群搭建
机器学习(十八):随机搜索和XGBoost
租房小程序登顶码云热门
R语言缺失时间序列的填充及合并:补齐时间序列数据中所有缺失的时间索引、使用merge函数合并日期补齐之后的时间序列数据和另外一个时间序列数据(补齐左侧数据)
西西成语接龙小助手
jMeter Transaction Controller 学习笔记
使用Redis做某个时间段在线数统计
动态数组底层是如何实现的
88. (the home of cesium) cesium polymerization figure
SAP ABAP SteammPunk 蒸汽朋克的最新进展 - 嵌入式蒸汽朋克
Cron表达式
8月5日,麒麟信安邀您相约鲲鹏开发者创享日·长沙站!
Codeforces Round #811 (Div. 3)
mysqlbinlog 超过500g自动删除,保留7个,求大深给个版本