小言_互联网的博客

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

891人阅读  评论(0)

救命啊,来个公司收了我吧,我都面试三个月了

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的运行内存



转载:https://blog.csdn.net/weixin_43122090/article/details/105299403
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场