JVM(Java Virtual Machine)
java内存
1. 内存共享的:堆、方法区
堆:存放的是java中的对象
方法区:存放类信息;存放类信息;字面量(值1288,inta=388,字符串,字符串常量池),静态的常量();
- 在jdk1.6及之前,字符串常量池是属于运行时常量池中的
- jdk1.7,也就是上面说的,字符串常量池从方法区中被单独拿到堆中了
来我们聊聊堆内存的分配以及对象的回收
(此处涉及对象存活判定方法(Dead or Live)以及GC(GarbageCollection)垃圾回收算法:看这篇https://blog.csdn.net/weixin_43889487/article/details/121945108)
为什么要划分新生代和老年代?
-
因为有的对象存活时间长,有的对象存活时间段,如果在同一存储空间,进行垃圾回收,会导致内存空间碎片化的问题,以及回收效率低。
-
不同的区采用不同的垃圾收集算法。寿命短的区清理频次高一点,寿命长的区清理频次低一点。提高效率。所以就有了新生代(标记复制)和老年代(标记清除/标记整理)。
为什么堆内 有两个survivor即s0和s1? 为什么新生代内存划分是8:1:1
为什么有两个survivor
假设我们只使用一个survivor区进行存活对象的复制,刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。
问题来了:下一次Eden满了的时候,此时进行Minor GC,Eden和Survivor各有一些存活对象,因为只有一个Survivor,所以Eden第二次的gc发现的存活对象也是放在唯一的一个Survivor区域中。但此时把Eden区的存活对象硬放到Survivor区(注意:survivor区的对象也会死),很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。
那么要怎么解决这个问题呢?
- 试图将Eden区存活的对象转移到survivor中,努力适应这种不连续的空间。但是不连续的空间会导致再分配大对象的时候,由于没有连续的空间来分配,会导致提前垃圾回收。
- 将survivor中的所有存活对象向下移动来消除碎片,然后将所有的存活对象移入其中。这样做会降低效率。
- 把两个区域中的所有存活对象都转移到完全独立的空间中,也就是第二块Survivor中,这样就可以留出一块完全空着的Eden和Survivor了,下次GC的时候再重复这个流程
为什么新生代内存Eden和survivor划分是8:1
- HotPost虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有百分之10%的内存会被浪费。
- 当Survivor空间不够放的时候,需要依赖其他内存(这里指老年代)进行分配担保。
2. 线程独享的:
虚拟机栈(每个线程有):每调用一次方法,都会存一次栈帧,
·java方法每调用一次就像虚拟机栈中压入一个栈元素,决定方法的执行;
·栈帧中包含局部变量表(存的是通过参数传过去的,自己在方法里面声明的局部变量),markword。本地方方法栈(每个线程)
•执行本地方法需要执行的一些信息数据;和虚拟机栈非常类似;
•Object中的HashCode (),clone(),arrayList.copy0;
非本地方法栈有哪些?
•自己写的Student类里边的get方法就是非本地方法
·有声明为native的接口(java中找不到没有实现类),就是本地方法。
。程序计数器:下一行执行哪个代码
拓展:
内存泄露
内存泄露为程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光,
广义并通俗的说,就是:不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄露。
内存溢出
简单来说:垃圾清理不及时,假设程序启动我需要500M内存,但内存只剩200M 明显不够用,这时候就会报错
内存泄漏和内存溢出区别与联系
- 内存泄漏:系统分配的内存无法被回收。
- 不一定报错。解决:检查程序,重启jvm
- 内存溢出:分配的内存空间超过系统内存。
- 一定报错。比如需要200M空间,程序启动用了500M就报错了;