超硬核!!!一篇文章掌握整个JVM的垃圾回收系统

救命啊,来个公司收了我吧,我都面试三个月了
在这里插入图片描述

JVM的垃圾回收机制

垃圾回收机制简称GC

GC主要用于Java堆的管理。Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。

1 什么是垃圾回收机制

程序在运行过程中,会产生大量的内存垃圾(一些没有引用指向的内存对象都属于内存垃圾,因为这些对象已经无法访问,程序用不了它们了,对程序而言它们已经死亡),为了确保程序运行时的性能,java虚拟机在程序运行的过程中不断地进行自动的垃圾回收(GC)。

GC是不定时去堆内存中清理不可达对象。不可达的对象并不会马上就会直接回收, 垃圾收集器在一个Java程序中的执行是自动的,不能强制执行清楚那个对象,即使程序员能明确地判断出有一块内存已经无用了,是应该回收的,程序员也不能强制垃圾收集器回收该内存块。程序员唯一能做的就是通过调用System.gc 方法来"建议"执行垃圾收集器,但是他是否执行,什么时候执行却都是不可知的。这也是垃圾收集器的最主要的缺点。当然相对于它给程序员带来的巨大方便性而言,这个缺点是瑕不掩瑜的。

手动执行GC:

System.gc(); // 手动回收垃圾

2 finalize方法作用

  1. finalize()方法是在每次执行GC操作之前时会调用的方法,可以用它做必要的清理工作。
  2. 它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

代码示例

package com.lijie;

public class Test {
	public static void main(String[] args) {
		Test test = new Test();
		test = null;
		System.gc(); // 手动回收垃圾
	}

	@Override
	protected void finalize() throws Throwable {
		// gc回收垃圾之前调用
		System.out.println("gc回收垃圾之前调用的方法");
	}
}

3 新生代、老年代、永久代(方法区)的区别

  1. Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。

  2. 在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。

先不要管为什么要分代,后面有例子

  1. 老年代就一个区域。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

  2. 这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。

  3. 默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。

  4. 其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 From Survivor 和 ToSurvivor ,以示区分。

  5. 默认的,Edem : From Survivor : To Survivor = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,From Survivor = To Survivor = 1/10 的新生代空间大小。

  6. JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。

  7. 因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。

  8. 永久代就是JVM的方法区。在这里都是放着一些被虚拟机加载的类信息,静态变量,常量等数据。这个区中的东西比老年代和新生代更不容易回收。

3.1 为什么要这样分代:

其实主要原因就是可以根据各个年代的特点进行对象分区存储,更便于回收,采用最适当的收集算法:

  1. 新生代中,每次垃圾收集时都发现大批对象死去,只有少量对象存活,便采用了复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

  2. 而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须采用“标记-清理”或者“标记-整理”算法。

新生代又分为Eden和Survivor (From与To,这里简称一个区)两个区。加上老年代就这三个区。数据会首先分配到Eden区当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。当Eden没有足够空间的时候就会触发jvm发起一次Minor GC,。如果对象经过一次Minor-GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代中了,当然晋升老年代的年龄是可以设置的。

3.2 Minor GC、Major GC、Full GC区别及触发条件
  1. Minor GC是新生代GC,指的是发生在新生代的垃圾收集动作。由于java对象大都是朝生夕死的,所以Minor GC非常频繁,一般回收速度也比较快。

  2. Major GC是老年代GC,指的是发生在老年代的GC,通常执行Major GC会连着Minor GC一起执行。Major GC的速度要比Minor GC慢的多。

  3. Full GC是清理整个堆空间,包括年轻代和老年代

Minor GC 触发条件一般为:

  1. eden区满时,触发MinorGC。即申请一个对象时,发现eden区不够用,则触发一次MinorGC。
  2. ​ 新创建的对象大小 > Eden所剩空间

Major GC和Full GC 触发条件一般为:
Major GC通常是跟full GC是等价的

  1. 每次晋升到老年代的对象平均大小>老年代剩余空间
  2. MinorGC后存活的对象超过了老年代剩余空间
  3. 永久代空间不足
  4. 执行System.gc()
  5. CMS GC异常
  6. 堆内存分配很大的对象

4 如何判断对象是否存活

4.1 引用计数法
  1. 引用计数法就是如果一个对象没有被任何引用指向,则可视之为垃圾。这种方法的缺点就是不能检测到环的存在。

  2. 首先需要声明,至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存。

  3. 什么是引用计数法:每个对象在创建的时候,就给这个对象绑定一个计数器。每当有一个引用指向该对象时,计数器加一;每当有一个指向它的引用被删除时,计数器减一。这样,当没有引用指向该对象时,计数器为0就代表该对象死亡

引用计数法的优点:

  • 引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,

引用计数法的缺点:

  • 主流的Java虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。
  • 例如:
package com.lijie;

public class Test {
	public Object object = null;
	public static void main(String[] args) {
		Test a = new Test();
		Test b = new Test();
		/**
		 * 循环引用,此时引用计数器法失效
		 */
		a.object = b;
		b.object = a;

		a = null;
		b = null;
	}
}

引用计数法的应用场景:

  • 建议不要用

4.4.2 可达性分析法
  1. 该种方法是从GC Roots开始向下搜索,搜索所走过的路径为引用链。当一个对象到GC Roots没用任何引用链时,则证明此对象是不可用的,表示可以回收。
    在这里插入图片描述

  2. 上图上图中Object1、Object2、Object3、Object4、Object5到GC Roots是可达的,表示它们是有引用的对象,是存活的对象不可以进行回收

  3. Object6、Object7、Object8虽然是互相关联的,但是它们到GC Roots是不可达的,所以他们是可以进行回收的对象。

那些可以作为GC Roots 的对象:

1、虚拟机栈(栈帧中的本地变量表)中引用的对象;
2、方法区中类静态属于引用的对象;
3、方法区中常量引用的对象;
4、本地方法栈中JNI(即一般说的Native方法)引用的对象。
等

可达性算法的优点:

  • 解决相互循环引用问题。

可达性算法的优点:

  • 目前和引用计数法比没得缺点

可达性算法的应用场景:

  • 这是目前主流的虚拟机都是采用的算法

5 垃圾回收机制策略(也称为GC的算法)

5.1 引用计数算法(Reference counting)

每个对象在创建的时候,就给这个对象绑定一个计数器。每当有一个引用指向该对象时,计数器加一;每当有一个指向它的引用被删除时,计数器减一。这样,当没有引用指向该对象时,计数器为0就代表该对象死亡,这时就应该对这个对象进行垃圾回收操作。

引用计数法的优点:

  • 引用计数算法的实现简单,判定效率也很高。

引用计数法的缺点:

  • 主流的Java虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。
  • 例如:
package com.lijie;

public class Test {
	public Object object = null;
	public static void main(String[] args) {
		Test a = new Test();
		Test b = new Test();
		/**
		 * 循环引用,此时引用计数器法失效
		 */
		a.object = b;
		b.object = a;

		a = null;
		b = null;
	}
}

引用计数法的应用场景:

  • 建议不要用

5.2 标记–清除算法(Mark-Sweep)

为每个对象存储一个标记位,记录对象的状态(活着或是死亡)。
分为两个阶段,一个是标记阶段,这个阶段内,为每个对象更新标记位,检查对象是否死亡;第二个阶段是清除阶段,该阶段对死亡的对象进行清除,执行 GC 操作。

标记清除算法的优点:

  • 是可以解决循环引用的问题
  • 必要时才回收(内存不足时)

标记清除算法的缺点:

  • 回收时,应用需要挂起,也就是stop the world。
  • 标记和清除的效率不高,尤其是要扫描的对象比较多的时候
  • 会造成内存碎片(会导致明明有内存空间,但是由于不连续,申请稍微大一些的对象无法做到),

标记清除算法的应用场景:

  • 该算法一般应用于老年代,因为老年代的对象生命周期比较长。

5.3 标记–整理算法

标记清除算法和标记压缩算法非常相同,但是标记压缩算法在标记清除算法之上解决内存碎片化(有些人叫"标记整理算法"为"标记压缩算法")

标记-整理法是标记-清除法的一个改进版。同样,在标记阶段,该算法也将所有对象标记为存活和死亡两种状态;不同的是,在第二个阶段,该算法并没有直接对死亡的对象进行清理,而是将所有存活的对象整理一下,放到另一处空间,然后把剩下的所有对象全部清除。这样就达到了标记-整理的目的。

标记–整理算法优点:

  • 解决标记清除算法出现的内存碎片问题,

标记–整理算法缺点:

  • 压缩阶段,由于移动了可用对象,需要去更新引用。

标记–整理算法应用场景:

  • 该算法一般应用于老年代,因为老年代的对象生命周期比较长。

5.4 复制算法

该算法将内存平均分成两部分,然后每次只使用其中的一部分,当这部分内存满的时候,将内存中所有存活的对象复制到另一个内存中,然后将之前的内存清空,只使用这部分内存,循环下去。

这个算法与标记-整理算法的区别在于,该算法不是在同一个区域复制,而是将所有存活的对象复制到另一个区域内。

复制算法的优点:

  • 在存活对象不多的情况下,性能高,能解决内存碎片和java垃圾回收算法之-标记清除 中导致的引用更新问题。

复制算法的缺点::

  • 会造成一部分的内存浪费。不过可以根据实际情况,将内存块大小比例适当调整;如果存活对象的数量比较大,复制算法的性能会变得很差。

复制算法的应用场景:

  • 复制算法一般是使用在新生代中,因为新生代中的对象一般都是朝生夕死的,存活对象的数量并不多,这样使用复制算法进行拷贝时效率比较高。
  • jvm将Heap(堆)内存划分为新生代与老年代。又将新生代划分为Eden与2块Survivor Space(幸存者区) ,然后在Eden –>Survivor Space 与To Survivor之间实行复制算法。
  • 不过jvm在应用复制算法时,并不是把内存按照1:1来划分的,这样太浪费内存空间了。一般的jvm都是8:1。也即是说,Eden区:From区:To区域的比例是始终有90%的空间是可以用来创建对象的,而剩下的10%用来存放回收后存活的对象。

5.5 分代算法(主要的算法就是上面四种,这个是附加的)

这种算法,根据对象的存活周期的不同将内存划分成几块,新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。可以用抓重点的思路来理解这个算法。
新生代对象朝生夕死,对象数量多,只要重点扫描这个区域,那么就可以大大提高垃圾收集的效率。另外老年代对象存储久,无需经常扫描老年代,避免扫描导致的开销。

新生代

  • 在新生代,每次垃圾收集器都发现有大批对象死去,只有少量存活,采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

老年代

  • 而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须“标记清除法或者标记整理算法进行回收。

垃圾收集器

1 什么是垃圾收集器?

  • 垃圾收集器是垃圾回收算法(引用计数法、标记清楚法、标记整理法、复制算法)的具体实现,不同垃圾收集器、不同版本的JVM所提供的垃圾收集器可能会有很在差别。
  • 我这以JDK8为准:
    在这里插入图片描述

图中展示了7种不同分代的收集器:
Serial、ParNew、Parallel Scavenge、CMS、Serial Old、Parallel Old、G1

而它们所处区域,则表明其是属于新生代还是老年代的收集器:

  • 新生代收集器:Serial、ParNew、Parallel Scavenge

  • 老年代收集器:CMS、Serial Old、Parallel Old

  • 整堆收集器:G1

两个收集器间有连线,表明它们可以搭配使用:

Serial / Serial Old
Serial / CMS
ParNew / Serial Old
ParNew / CMS
Parallel Scavenge / Serial Old
Parallel Scavenge / Parallel Old
G1

5.2 垃圾回收器详解

垃圾回收器 工作区域 回收算法 工作线程 用户线程并行 描述
Serial 新生带 复制算法 单线程 Client模式下默认新生代收集器。简单高效
ParNew 新生带 复制算法 多线程 Serial的多线程版本,Server模式下首选, 可搭配CMS的新生代收集器
Parallel Scavenge 新生带 复制算法 多线程 目标是达到可控制的吞吐量
Serial Old 老年带 标记-整理 单线程 Serial老年代版本,给Client模式下的虚拟机使用
Parallel Old 老年带 标记-整理 多线程 Parallel Scavenge老年代版本,吞吐量优先
CMS 老年带 标记-清楚 多线程 追求最短回收停顿时间
G1 新生带 + 老年带 标记-整理 + 复制算法 多线程 JDK1.9默认垃圾收集器
5.2.1 Serial
  • Serial 收集器:新生代。发展历史最悠久的收集器。它是一个单线程收集器,它只会使用一个 CPU 或者线程去完成垃圾收集工作,而且在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。

特点:

  1. 新生代收集器,使用复制算法收集新生代垃圾。
  2. 单线程的收集器,GC工作时,其它所有线程都将停止工作。
  3. 简单高效,适合单 CPU 环境。单线程没有线程交互的开销,因此拥有最高的单线程收集效率。

使用方式:

//如何设置JVM参数底下会讲解:这里只是列举一部分参数:

设置垃圾收集器:"-XX:+UseSerialGC"  --添加该参数来显式的使用改垃圾收集器;

5.2.2 ParNew
  • ParNew 收集器:新生代。Serial 的多线程版本,即同时启动多个线程去进行垃圾收集。

特点:

  1. 新生代收集器。ParNew垃圾收集器是Serial收集器的多线程版本,采用复制算法。
  2. 除了多线程外,其余的行为、特点和Serial收集器一样。
  3. 只有它能与 CMS 收集器配合使用。
  4. 但在单个CPU环境中,不比Serail收集器好,多线程使用它比较好。

使用方式:

//如何设置JVM参数底下会讲解:这里只是列举一部分参数:

设置垃圾收集器:"-XX:+UseParNewGC"  --强制指定使用ParNew;    
设置垃圾收集器: "-XX:+UseConcMarkSweepGC"  --指定使用CMS后,会默认使用ParNew作为新生代收集器;
设置垃圾收集器参数:"-XX:ParallelGCThreads"  --指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;

5.2.3 Parallel Scavenge
  • Parallel Scavenge 收集器:新生代。和 ParNew 的关注点不一样,该收集器更关注吞吐量,尽快地完成计算任务。

特点:

  1. 新生代收集器。
  2. 采用复制算法。
  3. 多线程收集。
  4. 与ParNew 不同的是:高吞吐量为目标,(减少垃圾收集时间,让用户代码获得更长的运行时间)

使用方式:

//如何设置JVM参数底下会讲解:这里只是列举一部分参数:

设置垃圾收集器:"-XX:+UseParallelGC"  --添加该参数来显式的使用改垃圾收集器;
设置垃圾收集器参数:"-XX:MaxGCPauseMillis"  --控制垃圾回收时最大的停顿时间(单位ms)
设置垃圾收集器参数:"-XX:GCTimeRatio"  --控制程序运行的吞吐量大小吞吐量大小=代码执行时间/(代码执行时间+gc回收的时间)
设置垃圾收集器参数:"-XX:UseAdaptiveSizePolicy"  --内存调优交给虚拟机管理

5.2.4 Serial Old
  • Serial Old 收集器:Serial 的老年代版本,使用标记 - 整理算法。

特点:

  1. 老年代收集器, 采用"标记-整理"算法。
  2. 单线程收集。

使用方式:

//如何设置JVM参数底下会讲解:这里只是列举一部分参数:

在JDK1.5及之前,与Parallel Scavenge收集器搭配使用,
在JDK1.6后有Parallel Old收集器可搭配。
现在的作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用

5.2.5 Parallnel old
  • Parallnel old 收集器,多线程:Parallel 的老年代版本,使用标记 - 整理算法。

特点:

  1. 针对老年代。
  2. 采用"标记-整理"算法。
  3. 多线程收集。
  4. 但在单个CPU环境中,不比Serial Old收集器好,多线程使用它比较好。

使用方式:

//如何设置JVM参数底下会讲解:这里只是列举一部分参数:

 设置垃圾收集器:"-XX:+UseParallelOldGC":指定使用Parallel Old收集器;

5.2.6 CMS
  • CMS 收集器:老年代。是一种以获取最短回收停顿时间为目标的收集器,适用于互联网站或者 B/S 系统的服务端上。

特点:

  1. 针对老年代,采用标记-清楚法清除垃圾;
  2. 基于"标记-清除"算法(不进行压缩操作,产生内存碎片);
  3. 以获取最短回收停顿时间为目标;
  4. 并发收集、低停顿;
  5. CMS收集器有3个明显的缺点:1.对CPU资源非常敏感、2.无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败、3.产生大量内存碎片
  6. 垃圾收集线程与用户线程(基本上)可以同时工作

使用方式:

//如何设置JVM参数底下会讲解:这里只是列举一部分参数:

设置垃圾收集器:"-XX:+UseConcMarkSweepGC":指定使用CMS收集器;

5.2.7 G1
  • G1 收集器:分代收集器。当今收集器技术发展最前沿成果之一,是一款面向服务端应用的垃圾收集器。G1可以说是CMS的终极改进版,解决了CMS内存碎片、更多的内存空间登问题。虽然流程与CMS比较相似,但底层的原理已是完全不同。

特点:

  1. 能充分利用多CPU、多核环境下的硬件优势;
  2. 可以并行来缩短(Stop The World)停顿时间;
  3. 也可以并发让垃圾收集与用户程序同时进行;
  4. 分代收集,收集范围包括新生代和老年代
  5. 能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;
  6. 能够采用不同方式处理不同时期的对象;
  7. 应用场景可以面向服务端应用,针对具有大内存、多处理器的机器;
  8. 采用标记-整理 + 复制算法来回收垃圾

使用方式:

//如何设置JVM参数底下会讲解:这里只是列举一部分参数:

设置垃圾收集器:"-XX:+UseG1GC":指定使用G1收集器;
设置垃圾收集器参数:"-XX:InitiatingHeapOccupancyPercent":当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45;
设置垃圾收集器参数:"-XX:MaxGCPauseMillis":为G1设置暂停时间目标,默认值为200毫秒;
设置垃圾收集器参数:"-XX:G1HeapRegionSize":设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region

JVM参数配置(在此可配置不同的GC回收器)

1 JVM内存参数简述

#常用的设置
-Xms:初始堆大小,JVM 启动的时候,给定堆空间大小。 

-Xmx:最大堆大小,JVM 运行过程中,如果初始堆空间不足的时候,最大可以扩展到多少。 

-Xmn:设置堆中年轻代大小。整个堆大小=年轻代大小+年老代大小+持久代大小。 

-XX:NewSize=n 设置年轻代初始化大小大小 

-XX:MaxNewSize=n 设置年轻代最大值

-XX:NewRatio=n 设置年轻代和年老代的比值。如: -XX:NewRatio=3,表示年轻代与年老代比值为 1:3,年轻代占整个年轻代+年老代和的 1/4 

-XX:SurvivorRatio=n 年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。8表示两个Survivor :eden=2:8 ,即一个Survivor占年轻代的1/10,默认就为8

-Xss:设置每个线程的堆栈大小。JDK5后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。

-XX:ThreadStackSize=n 线程堆栈大小

-XX:PermSize=n 设置持久代初始值	

-XX:MaxPermSize=n 设置持久代大小
 
-XX:MaxTenuringThreshold=n 设置年轻带垃圾对象最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。

#下面是一些不常用的

-XX:LargePageSizeInBytes=n 设置堆内存的内存页大小

-XX:+UseFastAccessorMethods 优化原始类型的getter方法性能

-XX:+DisableExplicitGC 禁止在运行期显式地调用System.gc(),默认启用	

-XX:+AggressiveOpts 是否启用JVM开发团队最新的调优成果。例如编译优化,偏向锁,并行年老代收集等,jdk6纸之后默认启动

-XX:+UseBiasedLocking 是否启用偏向锁,JDK6默认启用	

-Xnoclassgc 是否禁用垃圾回收

-XX:+UseThreadPriorities 使用本地线程的优先级,默认启用	

等等等......

2 JVM的GC收集器设置

-XX:+UseSerialGC:设置串行收集器,年轻带收集器 

 -XX:+UseParNewGC:设置年轻代为并行收集。可与 CMS 收集同时使用。JDK5.0 以上,JVM 会根据系统配置自行设置,所以无需再设置此值。

-XX:+UseParallelGC:设置并行收集器,目标是目标是达到可控制的吞吐量

-XX:+UseParallelOldGC:设置并行年老代收集器,JDK6.0 支持对年老代并行收集。 

-XX:+UseConcMarkSweepGC:设置年老代并发收集器

-XX:+UseG1GC:设置 G1 收集器,JDK1.9默认垃圾收集器

3 JVM参数在哪设置

3.1 IDEA在哪里设置JVM参数

1、单个项目的应用
在这里插入图片描述
在这里插入图片描述
2、全局的配置

  1. 找到IDEA安装目录中的bin目录
  2. 找到idea.exe.vmoptions文件
  3. 打开该文件编辑并保存。
    在这里插入图片描述
3.2 Eclipse在哪里设置JVM参数

1、配置单个项目

点击绿色图标右边的小箭头
在这里插入图片描述
在点击:Run Configurations ->VM arguments
在这里插入图片描述

2、配置全局JVM参数
修改Eclipse的配置文件,在eclipse安装目录下的:eclipse.ini文件
在这里插入图片描述

3.3 war(Tomcat)包在哪里设置JVM参数

war肯定是部署在Tomcat上的,那就是修改Tomcat的JVM参数

1、在Windows下就是在文件/bin/catalina.bat,
增加如下设置:JAVA_OPTS(JAVA_OPTS,就是用来设置 JVM 相关运行参数的变量)

set "JAVA_OPTS=-Xms512M -Xmx1024M ...等等等 JVM参数"

在这里插入图片描述

2、Linux要在tomcat 的bin 下的catalina.sh 文件里添加

注意:位置要在cygwin=false前
JAVA_OPTS="-Xms512M -Xmx1024M ...等等等 JVM参数"

在这里插入图片描述

3.4 Jar包在哪里设置JVM参数

Jar包简单,一般都是SpringBoot项目打成Jar包来运行

#运行时java -jar是直接插入JVM命令就好了
java -Xms1024m -Xmx1024m ...等等等 JVM参数 -jar springboot_app.jar & 

4 调优总结

  1. 在实际工作中,我们可以直接将初始的堆大小与最大堆大小相等,
    这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率。
  2. 初始堆值和最大堆内存内存越大,吞吐量就越高,
    但是也要根据自己电脑(服务器)的实际内存来比较。
  3. 最好使用并行收集器,因为并行收集器速度比串行吞吐量高,速度快。
    当然,服务器一定要是多线程的
  4. 设置堆内存新生代的比例和老年代的比例最好为1:2或者1:3。
    默认的就是1:2
  5. 减少GC对老年代的回收。设置生代带垃圾对象最大年龄,进量不要有大量连续内存空间的java对象,因为会直接到老年代,内存不够就会执行GC

注释:其实最主要的还是服务器要好,你硬件都跟不上,软件再好都没用
注释:老年代GC很慢,新生代没啥事
注释:默认的JVM堆大小好像是电脑实际内存的四分之一左右,

package com.lijie;

public class Test {
    public static void main(String[] args) {
        System.out.print("最大内存");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
    }
}

我的电脑是8G的运行内存
在这里插入图片描述

发布了78 篇原创文章 · 获赞 104 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/weixin_43122090/article/details/105332676