大家好 我是刘小乙。
今天和大家分享下,我之前在公司做培训时的Java虚拟机的知识,满满的都是干货,看下去一定有收获。
蓝色是内存
执行流程
1.加载类(classLoader)
会在方法区 扫描到 类结构,main方法(因为是静态的)
2.启动main
首先创建线程,并且在内部创建一个相应的栈(每一个栈都是为线程服务的,且存在由于线程内部 ,实际的存储是分开的)
每执行到的一个方法会以一个栈针的形式放入栈中
3.实例化过程
实例化过程,就是创建对象,首先会在堆里创建一个Employee对象,并且会把这个内存地址赋值给emp(就是持有对这个对象的引用)
4.给对象赋值,字符串 如emp.setName("小乙")
会在栈增加setName的栈针,“老齐”是字符串是静态的所以放在方法区里,同时让name持有的对象引用到“小乙”这个字符串
执行完之后,setName就会消失,因为执行完会马上从栈中弹出。
5,给对象赋值基本数据类型 如 emp.setAge(13);
由于13是按值引用的所以直接会被保存在emp的内存中。
6.在堆中创建对象
在堆中创建对象,并且栈中dept做引用
7.dept.setDname("小卖部");
将“小卖铺”这个字符串放在静态方法区,并且将引用赋值给dname
8.emp.setDepartment(dept);
就是employee中的departement属性引用到了Demartment的地址
9.emp.sayJoke("一言不合....");
首先创建sayJoke 的栈针,sayJoke中也调用了getName 方法,所以同样创建栈针,在方法区创建字符创“一言不合.....”,同时sayJoke也有一个打印的过程,所以创建println的栈针,并且生成新字符串“老齐说一言不合.....” 同时println对这个字符串进行引用。
方法执行完,首先ptintln 会被释放,之后是getName,最后是sayJoke
10.方法执行完成
main执行完,所以栈针消失。
此时栈中就没有任何东西,此时线程也就销毁了。
剩下的就垃圾回收的工作了。
java 堆内存模型与垃圾收集
简化理解
- 新生代:刚刚创建的对象,所以有可能存在许多的垃圾对象,所以应该优先被回收。
- 老年代:经过很多次清理之后你发现该对象依然有用,这个叫老年代
- 永久代:intern()方法入池的对象实际就在永久代当中,永久带是个bug,永久带不会被回收。除非jvm 崩溃,所以在jdk1.8之后将其更换为元空间(就是电脑的直接内存,假如电脑有100G内存空间,有80g给了堆内存,那么剩下20G都可以给元空间)。
在每一代的内存空间都会有一个伸缩区,那么该区域就可以由jvm根据空间使用情况动态扩充。当我们适当合理的设计了伸缩区的内存大小之后,就可以得到良好的性能提升,也就是说最容易的性能提升就是改变伸缩区内存大小设置。
对象创建于垃圾回收
java堆内存调整参数
频繁的GC一定会影响我们的性能,如何再能不平凡的发生GC那?
示范:首先取得当前可用内存空间量。
在整个分配给jvm堆内存使用之中,发现total--表示用户最大可用,max-total才表示伸缩区,每一块内存中都有一个伸缩区概念,如果给用户使用的内存空间留有伸缩区会造成以下一种情况:如果可能内存不足,是否伸缩区有空间,而后部分开辟伸缩区空间。(不够开辟,多了要回收,这个过程会造成性能下降)。
如果取消伸缩区,想办法让初始内存就是最大内存,这样就可以实现jvm性能调整,避免重复的内存操作,可以让整个代码执行速度上升。
如果想修改内存大小,可以使用的单位可以:K, M, G;但是要改需要考虑两种情况:
1,Eclipse修改,
2.在命令行方式下:在执行java程序前面追加相应信息
在整体参数中提供了一个GC处理详情的问题:-xx:+printGCDetails 会打印gc的处理过程
示范:
以上将程序处理gc的过程进行记录,我们以两条为例:
1.年轻代的gc处理
2.老年代的gc处理
3.会详细显示出内存使用情况
ps:此时内存状态只取得了供开发者观察的,但如果是项目运维人员是看不见这些内容的,如果想看见就必须使用一些监控程序,java有两类监控程序:
jdk安装目录下
这个是图形化监控(不好用)
3248是进程pid
今天分享就到这里,如果有什么问题和想要完整版的资料,请私信我,愿意和大家交流,并且把资料免费分享给大家。谢谢。