JVM原理自总结

一、成熟的系统调优
1、一定要绝对避免循环查数据库和缓存(PS:循环里面就不能有查询缓存,更不能有查询数据库的操作,因为循环的次数没法控制)

2、对于API接口的话,一般都是直接查缓存的,没有查数据库的

3、多用批量查询,少用单条查询,尽量一次查出来

4、对于使用阿里云,要留意一下相应产品的配置,该花的钱还是得花,同时,千万要记得正式环境中使用相应产品的内网地址

5、注意连接池大小(包括数据库连接池、Redis缓存连接池、线程池)

6、压测的机器上不要部署其它的服务,只跑待压测的服务,避免受其它项目影响;对于线上环境,最好一台机器上只部署一个重要的服务

7、没有用的以及被注释掉的代码,没有用的依赖最好及时清理掉

8、集群自不用说

9、一些监控类的工具工具可以帮助我们更好的定位问题,比如链路跟踪,这次项目中使用了PinPoint

10、如果技术上优化的空间已经非常小了,可以试着从业务上着手,用实际的数据说话,可以从日常的访问量,历史访问量数据来说服测试

11、每一次代码改动都有可能引入新的问题,因此,每次修改代码后都要回归测试一下(PS:每次修改完以后,我都会用几组不同的关键词搜索,然后比对修改前和修改后返回的数据是否一致,这个时候postman,以及Beyond compare就派上用场了)

12、关键的地方一定要多加点儿日志,方便以后排除问题,因为排查线上问题最主要还是靠日志

二、jvm 各种深入了解
引用计数算法
主流的Java虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。

可达性分析
GC roots对象向下搜索,搜索过的路径叫引用链,对象与GC roots 无关联,就被判定为为可回收对象。
java中,可作为GC roots的对象包括:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象。方法栈中JNI引用的对象。

强引用就是指在程序代码之中普遍存在的,类似“Object obj=new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

软引用是用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。

弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。

虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用。

finalize()方法,可以对处于GC中的对象进行一次拯救,即使是在F-Queue的队列中,只要和任何一个对象建立关联,或是给某个类变量或对象赋值,那就会回到弱引用。当然这样的拯救只有一次,因为finallize()只能被调用一次。

回收方法区(永久代),这里面的方法回收效率比较低,在堆中,尤其新生代,一次回收可以回收大概70%-90%的空间,永久代的垃圾回收效率很低

永久代回收有两部分内容:1.废弃常量和无用的类 2.回收废弃常量(类似于java堆中的对象)。

满足三个条件会被回收:1.该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。 2.加载该类的classloader已经被回收。 3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾收集算法:
1.标记清除算法,最起初的收集算法,后续算法都是针对这个算法进行改进得到的。存在的问题:1.效率不高。2.空间问题,大多导致以后程序运行过程中需要分配大对象时,无法找到足够的内存,导致提前出发垃圾回收。
2.复制算法,把内存划分为大小相同的两块,每次使用一块。当一块内存用完,就将存活对象复制到另一块,再把已使用的那块清除。 优势:实现简单,运行高效。 缺点:这种算法直接把内存缩小一半,代价太高,当对象存活率较高时要进行较多复制操作,效率会变低。用途:现在商业虚拟机基本都是采用复制算法来回收新生代。
3.标记整理算法:根据老年代的特点,标记整理算法应运而生,过程和标记清楚算法一样,但后续是让所有存货对象向一端移动,然后直接清理其他内存。
4.分代收集算法:当前商业虚拟机都是采用分代回收,根据对象存活周期的不同将内存划分为几块。一般是把java堆分为新生代老年代,这样就可以根据各年代特点适当的收集算法。新生代-----复制算法,老年代-----标记清理或标记整理。

CMS收集器
HotSpot:实现中使用一组oopmap的数据结构来达到目的,在类被加载的时候,hotspot九八对象内的偏移量上的数据类型计算出来,在编译过程中,也会在特定的位置记录下栈和寄存器哪些引用。
作用:GC在扫描的时候就得知信息。

安全点: 在hotspot内容变化的时候,并没有为每个指令生成oopmap,只是在特定位置记录这些信息,这些位置就是安全点,程序并不是所有地方都停下来GC,只有在安全点位置才能暂停。安全点决定了程序是否具有程序长时间执行的特征。


hotspot虚拟机的垃圾收集器:
7种不同的分代收集器:
Serial收集器 :jdk1.3.1之前新生代唯一选择.单线程收集器,他在进行垃圾收集时,所有工作县城都需要暂停,直到他收集结束。优点:简单高效,对于单个cpu环境来说,serial收集器没有其他线程开销,最高单线程收集效率。因为停顿时间可以控制在几十毫秒以内,不频繁发生完全可以接受,所以知道1.7 Serial收集器还是client模式下的虚拟机最好选择。

ParNew收集器: 他是Serial收集器的多线程版本。他是许多运行在server模式下的虚拟机首选的新生代收集器。有一个重要原因,目前只有它与CMS收集器配合工作。

Parallel Scavenge收集器: 新生代收集器,采用复制算法的收集器,并行多线程收集器。PS收集器的目标是达到一个可控制的吞吐量(Throuhtput)。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值。PS收集器又被称为吞吐量优先级收集器,它还有一个特别的参数:+UseAdaptiveSizePolicy参数。这个参数打开之后,就不用手动指定新生代大小,eden与survivor区的比例,晋升老年代对象年龄等细微参数。虚拟机会自动调节被称为GC自适应。自适应策略也是PS收集器和ParNew收集器一个重要区别。

Serial Old收集器: 是Serial老年代版本,同样是一个单线程收集器,使用标记整理算法。

Parallel Old收集器: Parallel Scavenge收集器老年版本,使用多线程和标记整理算法。

CMS收集器: 是一种以获取最短回收停顿时间为目的的收集器。希望停顿时间最短,来获得更好的用户体验。 使用标记清除算法,运作比较复杂,分为4个步骤分别是:初始标记,并发标记,重新标记,并发清除。1.初始标记,仅仅标记一下GC roots关联到的对象,速度很快,并发标记阶段就是进行GC rootsTracing的过程,二重新标记阶段是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的一部分对象的标记记录。优点:并发收集,低停顿,用户体验好。 缺点:1.CMS收集器对CPU资源非常敏感。导致一部分线程被占用,使应用程序变慢,吞吐量降低。2.CMS收集器无法处理浮动垃圾(并发清理阶段用户线程还在运行,这段时间就可能产生新的垃圾,新的垃圾在此次GC无法清除,只能等到下次清理)。3.基于标记清除算法,结束时会产生大量空间碎片垃圾。

G1收集器: 当今收集器技术发展最前沿成果之一。特点:1.并行与并发,通过并发的方式保持java运行不受GC影响。2.分代收集,根据存活时间来获取最好的收集效果。3.空间整合,G1收集器整体来看是基于标记整理算法,局部上来看是基于复制算法,意味着运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。有利于长时间运行,分配大对象时不会因为无法找到连续内存空间而提前GC。4.可预测的停顿,这是比CMS一大优势,能让使用者明确指定在一个长度的时间片内,这几乎已经是实时Java的垃圾收集器特征。


内存分配和回收策略:
1.对象优先在Eden分配
2.大对象直接进入老年代
3.长期存活的对象将进入老年代
4.动态对象年龄判断
5.分配担保

原文地址:https://www.cnblogs.com/lenlen/p/10626370.html