垃圾回收遇到了内存溢出

查了一张表,直接select * from table ,table有50万条数据。然后直接超时,后台报错内存溢出。遇到这个问题,好多人都说直接修改内存,增大内存。

首先了解下jvm调优。

Java堆(所有线程共享的一块内存区域)

1、在虚拟机启动时创建。存放对象实例。

2、GC堆,垃圾收集器管理的主要区域。

3、通过 -Xmx 和 -Xms 控制,无法再扩展时,将会抛出OutOfMemoryError异常。

内部分:新生代(1/3 的堆空间大小)、老年代(2/3 的堆空间大小)

新生代又分:Eden 和 两个 Survivor 区(2个Survivor分别叫from和to区 )

Edem : from : to = 8 : 1 : 1(大小比例)

回收流程:eden -->  回收后存活的 进入from ——> from满以后 ——> 拷贝仍在使用的放入to(新过来的数据也放入to),清空from区(一次年龄增加) ——> 数据年龄到一定上限时,存入老年代

(from 和 to 区是相互的,如果to在存数据时,两个身份交换) 

Minor GC:(Eden代满触发)回收年轻代。

Full GC:(老代满或永久代满触发)同时回收年轻代、年老代。

(注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)

Java虚拟机栈(Java方法执行的内存模型)


1、存储局部变量表、操作数栈、动态链接、方法出口等信息。

2、如果线程请求的栈深度大于虚拟机所允许的深度,则抛出StackOverflowError异常。

3、无法申请到足够的内存,就会抛出OutOfMemoryError异常。
 

本地方法栈(操作类似虚拟机栈)
为虚拟机使用到的native方法服务。

运行时常量池:(方法区的一部分,存放编译期生成的各种字面量和符号引用)

方法区(各个线程共享的内存区域)
已被虚拟机加载的类信息、常量、静态变量

直接内存
 

程序计数器(当前线程所执行的字节码的行号指示器。)
记录当前线程执行的位置,然后重新获取到cpu时间后,能接着上次任务执行。

执行引擎
负责执行虚拟机的字节码
 

垃圾收集系统

1、执行是自动的,调用System.gc 方法来"建议"执行垃圾收集器,但不一定会马上执行。回收类时会先调用类的finalize()

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("垃圾回收机制...");
	}
}

JVM参数
-XX:+PrintGC      每次触发GC的时候打印相关日志
-XX:+UseSerialGC      串行回收
-XX:+PrintGCDetails  更详细的GC日志
-Xms               堆初始值
-Xmx               堆最大可用值
-Xmn               新生代堆最大可用值
-XX:SurvivorRatio  用来设置新生代中eden空间和from/to空间的比例.
-XX:NewRatio       配置新生代与老年代占比 1:2
-XX:MaxTenuringThreshold(进入老年代需要的年龄,默认15)
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/export/home/tomcat/logs/(当出现OutOfMemoryError 时,把该内存日志打印到指定Path路径内)
 

总结:在实际工作中,我们可以直接将初始的堆大小与最大堆大小相等,

这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率。
 

使用示例:-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
说明:堆内存初始化值20m,堆内存最大值20m,新生代最大值可用1m,eden空间和from/to空间的比例为2/1
使用示例: -Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
-XX:NewRatio=2
说明:堆内存初始化值20m,堆内存最大值20m,新生代最大值可用1m,eden空间和from/to空间的比例为2/1
新生代和老年代的占比为1/2

垃圾收集器
serial收集器(-XX:+UseSerialGC,小型应用)

1、串行收集器,使用一个线程去回收。

2、新生代、老年代使用串行回收;新生代复制算法、老年代标记-压缩。

3、垃圾收集的过程中会Stop The World(服务暂停)

4、CPU利用率最高,停顿时间即用户等待时间比较长。

ParNew收集器(-XX:+UseParNewGC,-XX:ParallelGCThreads=8)

1、Serial收集器的多线程版本

2、新生代并行,老年代串行;新生代复制算法、老年代标记-压缩

parallel 收集器( XX:+USeParNewGC)

1、类似ParNew收集器,更关注系统的吞吐量。

2、自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调节参数。

3、控制GC的时间不大于多少毫秒或者比例。

4、采用多线程来通过扫描并压缩堆。

5、特点:停顿时间短,回收效率高,对吞吐量要求高。

6、适用场景:大型应用,科学计算,大规模数据采集等。

cms收集器(-XX:+UseConcMarkSweepGC)

1、以获取最短回收停顿时间为目标的收集器。(希望系统停顿时间最短,以给用户带来较好的体验)

2、流程:

(1)初始标记:(短暂,暂停所有应用程序线程)

(2)并发标记

(3)并发预处理

(4)重新标记:(短暂,暂停所有应用程序线程),标记并发标记阶段遗漏的对象(在并发标记阶段结束后对象状态的更新导致)

(5)并发清除

(6)并发重置

3、优点:响应时间优先,减少垃圾收集停顿时间

4、缺点:产生大量空间碎片、并发阶段会降低吞吐量

g1收集器( -XX:+UseG1GC)

1、堆被划分成 许多个连续的区域(region)。

2、吸收了CMS收集器特点。

3、特点:

  支持多CPU和垃圾回收线程
  在主线程暂停的情况下,使用并行收集(并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。 )
  在主线程运行的情况下,使用并发收集(并发是指一个处理器同时处理多个任务。)
4、优点:支持很大的堆,高吞吐量
 

回到遇到的问题上来,其实这个问题要看什么环境下出现的。

如果是环境硬是需要查询所有50万条数据,展示到页面,页面渲染能够秒级展示这50万条数据,或者前端能够对50万条数据做分页处理。那么这种情况下,解决办法只有jvm调优,增大内存。

如果环境只需要展示数据,并不要求一次性展示50万条数据,前端也不支持这么多条数据的渲染,那么,我们就应该加限制,比方说分页处理,条件限制等。减少前后端处理数据量。

我这边是一张报表,报表肯定是给人看的,一次性看几十万条数据,人是做不到的。所以我这边就限制每次请求,报表数据源的条数不能大于10万条,否则分页处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dmlcq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值