System.gc()

1.垃圾收集算法的核心思想

  Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。

  垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。

  2.触发主GC(Garbage Collector)的条件

  JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:

  ①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

  ②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

  由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。

  3.减少GC开销的措施

  根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面:

  (1)不要显式调用System.gc()

  此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。

  (2)尽量减少临时对象的使用

  临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。

  (3)对象不用时最好显式置为Null

  一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。

  (4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)

  由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如 Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer 是可变长的,它在原有基础上进行扩增,不会产生中间对象。

  (5)能用基本类型如Int,Long,就不用Integer,Long对象

  基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。

  (6)尽量少用静态对象变量

  静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

  (7)分散对象创建或删除的时间

  集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片, 从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC 的机会。

摘自:http://blog.youkuaiyun.com/bocaicbl/archive/2010/02/23/5319915.aspx

-------------------------------------------------------------

趁着今天写程序的热乎劲儿,小谈一下System.gc()这个神秘的东东~

很多人把Java的“效率低下”归咎于不能自由管理内存,但我们也知道将内存管理封装起来的好处,这里就不赘述。

Java中的内存分配是随着new一个新的对象来实现的,这个很简单,而且也还是有一些可以“改进”内存回收的机制的,其中最显眼的就是这个System.gc()函数。

乍一看这个函数似乎是可以进行垃圾回收的,可事实并不是那么简单。
其实这个gc()函数的作用只是提醒虚拟机:程序员希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行,而且具体什么时候进行是取决于具体的虚拟机的,不同的虚拟机有不同的对策。

那么下一个问题就是:gc()进行回收的准则是什么?也就是说什么样的对象可以被回收?
简单来说就是:没有被任何可达变量指向的对象。这里的可达是我发明的……意思就是能够找到的,那什么样的是不可达的呢?
比如说:
a.v = b;
b.v = c;
/*
 *Watch out !
 */
a.v = d;
看一下这段代码:
第一行:对象a的变量v指向了对象b
第二行:对象b的变量v指向了对象c
第六行:对象a的变量v指向了变量d。
这个时候,虽然变量c指向的对象有c 以及b.v指向它,但是它们都已经不可达了,为什么?因为唯一可以找到它们的是a.v,但是现在a.v指向了d,所以他们就是不可达的了。

理由也很直观:没有任何可达变量指向你,你还有活下去的理由吗?你就算活下去谁能找得到你呢?

所以说,C++中将释放了的指针置为null的习惯要保留到Java中,因为这有可能是你释放内存的唯一途径。

最后的箴言:不要频繁使用gc函数。
我的建议是:保持代码健壮(记得将不用的变量置为null),让虚拟机去管理内存。

摘自:

http://blog.youkuaiyun.com/huxin1/archive/2010/10/11/5932911.aspx

<think>好的,我现在需要帮助用户优化Java的System.gc(),也就是垃圾回收机制。首先,我得回忆一下用户的问题,他们想知道优化方法和最佳实践。用户可能遇到了性能问题,比如频繁的GC导致应用卡顿,或者想预防潜在的问题。 首先,我应该考虑System.gc()本身的问题。Java的System.gc()会显式触发Full GC,但这样做的缺点很多。比如,它会暂停整个应用,尤其是在使用Stop-The-World的回收器时,影响性能。而且,同的垃圾回收器对System.gc()的处理可能同,比如G1可能会忽略它,或者CMS会执行并发收集,但效果一定好。所以,用户可能误用了这个方法,导致性能问题。 接下来,我需要整理优化方法。第一步通常是建议避免显式调用System.gc(),因为JVM自己管理GC更高效。然后,调整JVM参数是关键,比如堆内存大小,年轻代和老年代的比例,选择合适的回收器如G1或者ZGC。另外,优化代码,减少对象创建,使用对象池,及时释放资源,这些都能减少GC压力。 然后,最佳实践方面,监控和分析GC日志是必须的。用户需要知道如何启用GC日志,使用工具分析日志,找出频繁GC的原因。另外,选择适合应用场景的GC算法也很重要,比如低延迟选G1或ZGC,高吞吐量用Parallel GC。 还要考虑用户的引用中提到的内容,比如同的垃圾回收器适用场景,性能优化中的内存管理和算法效率,以及监控系统运行状况的部分。需要将这些内容融入回答中,确保引用正确。 用户可能需要知道具体如何配置JVM参数,比如使用-XX:+DisableExplicitGC来禁用System.gc(),或者调整堆的大小。同时,代码层面的优化,比如避免内存泄漏,使用更高效的数据结构,这些也是关键点。 可能的问题点是,用户是否了解同的GC回收器,或者如何监控GC活动。所以,生成的相关问题应该涵盖这些方面,比如如何选择GC算法,如何分析GC日志,或者内存泄漏的排查方法。 最后,确保回答结构清晰,分点说明,引用正确的来源,比如引用[1]提到的GC回收器,引用[3]中的监控指标。同时,避免使用Markdown,用自然的中文段落,符合用户的要求。</think>### Java System.gc()优化方法与最佳实践 #### 一、System.gc()的核心问题 1. **显式触发Full GC** `System.gc()`会强制触发完整的垃圾回收(Full GC),可能导致长时间的应用停顿(Stop-The-World)。例如,在使用串行垃圾回收器(Serial GC)时,暂停时间可能达到秒级[^1]。 2. **可控性** 同垃圾回收器对`System.gc()`的响应同: - Parallel GC会立即执行Full GC - G1 GC可能忽略此调用 - CMS GC可能触发并发标记阶段但无法保证及时回收[^1] #### 二、优化方法 1. **避免显式调用System.gc()** ```java // 典型错误用法示例 public void processData() { // ...业务逻辑 System.gc(); // 可能导致性能波动 } ``` 可通过JVM参数禁用显式GC: ```bash -XX:+DisableExplicitGC ``` 2. **优化JVM参数配置** - **堆内存设置** ```bash -Xms4g -Xmx4g # 固定堆大小避免动态调整 -XX:NewRatio=2 # 年轻代与老年代比例 ``` - **GC算法选择** ```bash # 低延迟场景(如Web服务) -XX:+UseG1GC -XX:MaxGCPauseMillis=200 # 大内存场景(>16GB) -XX:+UseZGC ``` 3. **代码层优化** - 使用对象池减少临时对象创建 - 及时关闭资源(数据库连接、文件流) - 避免在循环中创建大对象 - 使用`WeakReference`/`SoftReference`管理缓存 #### 三、最佳实践 1. **GC监控与分析** - 启用GC日志记录: ```bash -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m ``` - 使用工具分析: - GCEasy(在线分析工具) - JVisualVM(本地监控) 2. **性能指标关注重点** | 指标 | 建议阈值 | 优化方向 | |---------------------|-------------------|------------------------| | Young GC频率 | <2次/秒 | 增大年轻代 | | Young GC耗时 | <50ms | 调整Survivor比例 | | Full GC频率 | 0次/小时 | 减少内存泄漏 | | Full GC最大耗时 | <1秒(G1/ZGC) | 升级GC算法 | 3. **环境变量配置建议** 根据引用[2]的建议: - 开发环境使用`JAVA_OPTS`设置通用参数 - 生产环境使用`CATALINA_OPTS`配置GC相关参数 ```bash CATALINA_OPTS="-Xmx4g -XX:+UseG1GC" ``` #### 四、典型问题排查流程 1. 通过`jstat -gcutil <pid> 1000`实时监控GC状态 2. 使用MAT(Memory Analyzer Tool)分析堆转储 3. 检查引用链确认内存泄漏(如图片未释放、未关闭的线程池) : 同垃圾回收器的选择需根据应用特性决定,如G1适合大堆内存场景 [^3]: 系统应监控Young GC频率和对象晋升速率,避免过早提升到老年代[^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值