原文路径:https://juejin.cn/post/6844903892774289421 🌟🌟🌟🌟🌟 {这个文章很不错;尤其是搭配后一个关于垃圾回收的文章,看起来很顺畅}

原文内容有点多,就不全部复制过来了,仅仅是将阅读时感觉有意思的部分拿过来了;后续直接看原文
看上边这个链接的内容的时候会对垃圾回收和 新生代 老年代、永久代等概念有依赖,那就来看看这个链接 https://www.cnblogs.com/cuijj/p/10499621.html

看的过程中会牵扯出 强引用 弱引用 软引用 虚引用的概念;这个参看另一个文章
4.4.18 强引用、弱引用区别


1 堆栈相关

image.png

  • JVM栈(Java Virtual Machine Stacks): Java中一个线程就会相应有一个线程栈与之对应,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈,因此栈存储的信息都是跟当前线程(或程序)相关信息的,包括局部变量程序运行状态方法返回值方法出口等等。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
  • 堆(Heap): 堆是所有线程共享的,主要是存放对象实例和数组。处于物理上不连续的内存空间,只要逻辑连续即可

    阶段总结: 堆是所有线程共享的,栈是线程独享的;

  • 方法区(Method Area): 属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

  • 常量池(Runtime Constant Pool): 它是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
  • 本地方法栈(Native Method Stacks)

    堆(Heap)和JVM栈程序运行的关键,因为:

    1. 栈是运行时的单位(解决程序的运行问题,即程序如何执行,或者说如何处理数据),而堆是存储的单位(解决的是数据存储的问题,即数据怎么放、放在哪儿)。
    2. 堆存储的是对象。栈存储的是基本数据类型和堆中对象的引用;(参数传递的值传递和引用传递)

    栈中存放的是对象的引用,指向了堆中的数据;这样的好处最起码就是符合分而治之的思想,并且堆中的数据可以被共享,多个线程指向同一个数据的话,那节省了一点点重复数据的空间啊;


2 堆栈关系

栈内存里面存储的都是局部变量。局部变量在定义时放入栈中,在退出其作用域后被从栈中移出,因此栈内存中更新很快。 堆内存里面存储的都是一个个的对象,都是new出来的(注意String类型的也存储在堆中,尽管有时候可能并没有显式地写new)。堆里面的对象不会被随意地释放,当某个对象已经没有任何指向其的引用的时候,它就变成了java里的垃圾,这时候它并没有马上被释放,而是要靠jvm的垃圾回收机制来释放,其释放时间就要根据垃圾回收机制的策略来定了。 还需要注意一点,当定义数组的时候,比如如下代码: int arr = new int[3]; 这里的arr变量是局部变量,会被存储到栈中,而数组的元素因为是new出来的,因此会存放于堆中,而arr中存放的就是这个数组的首地址。顺便提一句,存放在堆中的元素如果没有显式给定初始值,是会被自动赋予一个初始值的,比如0、null等等,而在栈中如果没有显示指定,是不会自动赋值的。

3 堆

Java堆是java虚拟机所管理内存最大的一块内存空间,处于物理上不连续的内存空间,只要逻辑连续即可,主要用于存放各种类的实例对象。该区域被所有线程共享,在虚拟机启动时创建,用来存放对象的实例,几乎所有的对象以及数组都在这里分配内存(栈上分配、标量替换优化技术的例外)。
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )老年代 ( Old )新生代 ( Young ) 又被划分为三个区域:EdenFrom Survivor(S0)To Survivor(S1)。如图所示:
堆的内存布局:
image.png
这样划分的目的是为了使jvm能够更好的管理内存中的对象,包括内存的分配以及回收。 而新生代按eden和两个survivor的分法,是为了

  • 有效空间增大,eden+1个survivor;
  • 有利于对象代的计算,当一个对象在S0/S1中达到设置的XX:MaxTenuringThreshold值后,会将其挪到老年代中,即只需扫描其中一个survivor。如果没有S0/S1,直接分成两个区,该如何计算对象经过了多少次GC还没被释放。
  • 两个Survivor区可解决内存碎片化

参数 描述

  1. -Xms 堆内存初始大小,单位m、g
  2. -Xmx 堆内存最大允许大小,一般不要大于物理内存的80%
  3. -Xmn 年轻代内存初始大小
  4. -Xss 每个线程的堆栈大小,即JVM栈的大小
  5. -XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值
  6. -XX:NewSzie(-Xns) 年轻代内存初始大小,可以缩写-Xns
  7. -XX:MaxNewSize(-Xmx) 年轻代内存最大允许大小,可以缩写-Xmx
  8. -XX:SurvivorRatio 年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1
  9. -XX:MinHeapFreeRatio GC后,如果发现空闲堆内存占到整个预估堆内存的40%,则放大堆内存的预估最大值,但不超过固定最大值。
  10. -XX:MaxHeapFreeRatio 预估堆内存是堆大小动态调控的重要选项之一。堆内存预估最大值一定小于或等于固定最大值(-Xmx指定的数值)。前者会根据使用情况动态调大或缩小,以提高GC回收的效率,默认70%
  11. -XX:MaxTenuringThreshold 垃圾最大年龄,设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率
  12. -XX:InitialTenuringThreshold 可以设定老年代阀值的初始值
  13. -XX:+PrintTenuringDistribution 查看每次minor GC后新的存活周期的阈值 Note: 每次GC 后会调整堆的大小,为了防止动态调整带来的性能损耗
  14. 一般设置-Xms、-Xmx 相等

新生代的三个设置参数:-Xmn,-XX:NewSize,-XX:NewRatio的优先级:
(1).最高优先级: -XX:NewSize=1024m和-XX:MaxNewSize=1024m
(2).次高优先级: -Xmn1024m (默认等效效果是:-XX:NewSize==-XX:MaxNewSize==1024m)
(3).最低优先级:-XX:NewRatio=2
推荐使用的是-Xmn参数,原因是这个参数很简洁,相当于一次性设定NewSize和MaxNewSIze,而且两者相等。


4 内存溢出分类

image.png


5 jvm调优相关

这里暂时都是很浅显的认知;
image.png
减少gc开销的措施: 代码上的、jvm参数上的
image.png

image.png
image.png 调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。jvm的调优也不例外,jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。