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

边栏推荐
- R语言glm函数使用频数数据构建二分类logistic回归模型,分析的输入数据为频数数据(多个分类指标对应的阴性样本和阳性样本的频数数据)、weights参数指定频数值
- 【LeetCode每日一题】——540.有序数组中的单一元素
- init和destory方法
- Catering Supply Chain Management System
- 化学制品制造业数智化供应链管理系统:打造智慧供应体系,赋能企业产效提升
- 北京海淀6家必胜客被暂停外卖订餐 存在食品安全问题
- 通关剑指 Offer——剑指 Offer II 010. 和为 k 的子数组
- Json的FastJson与Jackson
- C. LIS or Reverse LIS?
- Qt自动补全之QCompleter使用
猜你喜欢

php如何查询字符串以什么开头

华为云数据治理生产线DataArts,让“数据‘慧’说话”

Kotlin挂起函数原理是什么

谷歌开发者社区推荐:《Jetpack Compose 从入门到实战》新书上架,带你踏上 Compose 开发之旅~

Boost库学习笔记(一)安装与配置

What does the product system of a digital financial enterprise look like?

动态数组底层是如何实现的

开发一套高容错分布式系统

15 days to upgrade to fight monsters and become a virtual fashion creator

shell脚本详解-------循环语句wuile循环和until循环
随机推荐
乐享购(分享购)的模式:优势、亮点、收益
mysql学习笔记——利用动态SQL和Session变量实现一个公式或者计算器
(1), the sequential storage structure of linear table chain storage structure
win11如何退出安全模式
水能自发变成“消毒水”,83岁斯坦福教授:揭示冬天容易得流感的部分原因...
谷歌开发者社区推荐:《Jetpack Compose 从入门到实战》新书上架,带你踏上 Compose 开发之旅~
】 【 LeetCode daily one problem - 540. The order of a single element of the array
SAP 电商云 Spartacus UI 页面布局的设计原理
码蹄集 - MT2094 - 回文之时:第4组数据错误
机器学习(十七):网格搜索(Grid Search)和SVM
Boost library study notes (1) Installation and configuration
正则过滤字符串中 script 标签
CF86D Powerful array
【LeetCode Daily Question】——374. Guess the size of the number
软件测试高频面试题真实分享/网上银行转账是怎么测的,设计一下测试用例。
taro 滚动组件ScrollView
麒麟信安石勇博士荣获openEuler社区年度开源贡献之星
Flutter实战-请求封装(四)之gzip报文压缩
容器化 | 在 NFS 备份恢复 RadonDB MySQL 集群数据
NLP未来,路在何方?从学术前沿和业界热点谈起