上面堆运行时数据区描述了很多,其实重点存储数据是堆和方法区,所以内存的设计也着重从这两方面展开
对于虚拟机栈,本地方法栈,程序计数器都是线程私有的。
可以这样理解,jvm运行时数据区时一种规范,而jvm内存模式是对该规范的实现
2.4.2图形展示
一块非堆区,一块是堆区
堆区分为两大块,一个是old区,一个是young区
young区分为两大块,一个是Survivor区(S0+S1),一块是Eden区
S0和S1一样大,也可以叫From和To
一般情况下,新创建的对象都会被分配到Eden区,一些特殊的大的对象会直接分配到Old区。
我创建一个普通的java对象,我出生在Eden区,在Eden区我还看到和我长得很像的兄弟,我们在Eden区中玩了挺长时间。
有一天Eden区中的人实在太多,我们被迫去了Survivor区的From区,自从区了Survivor区,我就开始漂,有时候在Survivor区的From区,有时候在Survivor的To区,居无定所。直到我18岁的时候,我成年,该去社会闯闯。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的。
如何理解各种GC
Partial GC
Partial其实也是就是部分的意思,那么翻译过来也就是回收部分GC堆的模式,他并不会回收我们整个堆,而我们的young GC以及我们的Old GC都属于这种模式。
Young GC
只回收Young区
old GC
只回收old区
full GC
full实际上就是对于整体回收
如果没有Survivor,Eden区没进行一次Minor GC,存活的对象就会被送到老年代。这样一来,老年代很快被填满,触发Major GC(因为Major GC 一般伴随这Minor GC,也可以看做触发了 Full GC)。
老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。
可能你会说,那就对老年代的空间进行增加或减少
假如增加老年代空间,更多存活对象才能填满老年代,虽然降低Full GC频率,但是随着老年代空间加大,一般发生Full GC,执行所需要时间更长。假如减少老年代空间,虽然Full GC所需要时间减少,但是老年代很快被存活对象填满,Full GC 频率增加。
所以Survivalor 存活意义,就是减少被送到老年代的对象,进而减少Full GC的发生,SUrvivor的预筛选保证,只有经历16次Minor GC还能再新生代中存活的对象,才会被送到老年代。
最大的好处就是解决了碎片化。也就是说为什么一个Survivor区不行?第一部分中,我们直到了必须设置Survivor区,假设现在只有一个Survivor区,我们来模拟一下流程
刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循环下去,下一次Eden满了的时候,问题来,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区
的存活对象硬方到Survivor区,很明显这两部分对象所占有的内存是不连续也就是导致内存碎片化。永远有一个Sunrvivor space是空的,另外一个非空的Survivor space无碎片。
新生代中的可用内存:复制算法用来担当的内存为9:1
可用内存中Eden:S1区为8:1
即新生代中Eden:S1:S2=8:1:1
现代的商业虚拟机都采用这种收集算法来回收新生代,IBM公司的专门研究表明,新生代中的对象大概98%是朝生夕死的
jvm默认为每个线程在Eden上开辟一个buffer区域,用来加速对象的分配,称之为TLAB,全称:Thread Local Allocation Buffer.
对象优先会在TLAB上分配,但是TLAB空间通常会比较小,如果对象比较大,那么还是在共享区域分配。
使用visualvm
visualgc 插件下载连接https://visualvm.github.io/pluginscenters.html
选择对应jdk版本链接-》Tool-》visual GC
若上述链接找不到适合的,大家可以自己到网上下载对应的版本
比如方法区中添加Class的信息
asm依赖和Class代码
虚拟机栈
说明
Stack Space用来做方法的递归调用时压入Stack Frame(栈帧)。所以当递归调用太深的时候,就有可能耗尽Stack Space,爆出 StackOverflow的错误。
-Xss128:设置每个线程的堆栈大小。jdk 5以后每个线程堆栈大小为1m,以前每个线程堆栈大小为256k。根据应用的线程所需要内存大小进行调整。在相同物理内存下,减少这个值生成更多的线程,但是操作系统堆一个进程内的线程数还是有限制的,不能无限生成,经验值在3000-5000左右。
线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是该线程内有递归,大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创造栈的数量,如果是多线程的应用,就会出现内存溢出的错误。