Java 如何估计JVM是否有足够的空闲内存用于特定的数据结构?
我有以下情况:有两台机器组成一个集群。客户端可以加载数据集,我们需要选择将加载数据集的节点,如果没有一台机器可以安装数据集,则拒绝加载/避免OOM错误 我们目前所做的:我们现在在数据集中计算Java 如何估计JVM是否有足够的空闲内存用于特定的数据结构?,java,memory-management,out-of-memory,Java,Memory Management,Out Of Memory,我有以下情况:有两台机器组成一个集群。客户端可以加载数据集,我们需要选择将加载数据集的节点,如果没有一台机器可以安装数据集,则拒绝加载/避免OOM错误 我们目前所做的:我们现在在数据集中计算条目计数,并将要使用的内存估计为条目计数*经验系数(手动确定)。然后检查这是否低于可用内存(通过Runtime.freemory())获得),如果是,则加载它(否则在其他节点上重做进程/报告没有可用容量) 这种方法的问题是: 经验系数需要重新访问并手动更新 freemory有时可能会因为一些未清理的垃圾而漏
条目计数
,并将要使用的内存估计为条目计数*经验系数
(手动确定)。然后检查这是否低于可用内存(通过Runtime.freemory()
)获得),如果是,则加载它(否则在其他节点上重做进程/报告没有可用容量)
这种方法的问题是:
经验系数
需要重新访问并手动更新
freemory
有时可能会因为一些未清理的垃圾而漏报(这可以通过在每次这样的调用之前运行System.gc
来避免,但是这会降低服务器速度,并可能导致过早升级)
- 另一种方法是“尝试加载数据集”(如果抛出OOM,则返回),但是一旦抛出OOM,您可能会损坏在同一JVM中运行的其他线程,并且无法从中恢复
这个问题有更好的解决方案吗?可以将经验系数作为构建步骤进行计算,并将其放入属性文件中
虽然freemory()
几乎总是小于GC后的可用量,但如果maxMemory()
指示可能有足够的内存,可以检查它是否可用,并调用System.GC()
注意:在生产中使用System.gc()
,只会导致错误使用,通常会导致性能降低,并掩盖真正的问题
我将避免触发OOME,除非您运行的是JVM,您可以根据需要重新启动。我的解决方案:
如果除您的程序外没有其他进程正在运行,则将Xmx设置为物理机器的RAM的90%-95%
。对于32 GB RAM机器,将Xmx
设置为27MB-28MB
使用一种好的gc算法——CMS或G1GC并微调相关参数如果应用程序需要超过4 GB的RAM,我更喜欢G1GC
。如果选择G1GC,请参考此问题:
自己计算内存使用上限,而不是检查可用内存。添加已用内存和要分配的内存<代码>从你自己的上限中减去它,比如90%的Xmx
。如果您仍有可用内存,请批准内存分配请求
正如您正确指出的,使用
freemory
不会告诉您Java垃圾收集可以释放多少内存。您可以运行负载测试,并使用JConsole、VisualVM、jstat和JVM的printGCStats
选项等工具了解JVM堆使用模式和内存分配、取消分配模式。这将给出一个更准确地计算经验系数的想法,基本上了解java应用程序可以处理的负载模式。下一步是选择正确的GC并调整基本GC设置以提高效率。这不是一个快速的解决方案,但从长远来看可能是一个更好的解决方案
另一种方法是使用JVM设置杀死JVM,一旦OOM发生,然后编写、恢复一个简单的进程监视脚本,以便在JVM关闭时启动JVM。另一种方法是将每个数据负载隔离在自己的JVM中。您只需预定义每个JVM的最大堆大小,依此类推,然后设置每个主机的JVM数量,使每个JVM都可以占用其最大堆大小。这将使用更多的资源-这意味着你不能通过填充较低的内存数据负载来利用内存的每一个字节-但它极大地简化了问题(并降低了出错的风险),它使判断何时/是否需要添加新主机变得可行,最重要的是,它减少了任何一个客户端对所有其他客户端的影响
使用这种方法,给定的JVM要么“忙”,要么“可用”
在任何给定的数据加载完成后,相关JVM可以声明自己可用于新的数据加载,也可以直接关闭。(无论哪种方式,您都希望有一个单独的进程来监视JVM,并确保始终运行正确的数字。)
客户端可以加载数据集,我们需要选择
将加载数据集并拒绝加载/避免OOM错误,如果
没有一台机器可以适应数据集
这是一个作业调度问题,即我有有限的资源,我们如何最好地利用它们。我会在接近尾声时解决OOM问题
我们有一个主要因素,即RAM,但解决调度问题取决于许多因素,如
作业是小作业还是大作业,即在一个节点上或两个或三个节点上运行成百上千个作业。想想Linux调度器
他们是否需要在特定的时间范围内完成?实时调度程序
考虑到我们在一份工作开始时所知道的一切,我们能预测一份工作何时会在某个时间范围内结束吗?如果我们可以预测,在节点X上,我们每15-20秒释放100MB的空间,我们就有办法在该节点上安排200Mb的作业,即我相信在40秒内,我将在该节点上完成200Mb的空间,40秒是提交作业的个人或机器可以接受的限制
假设我们有一个函数,如下所示
predicted_time predict(long bytes[, factors]);
predicted_time predict(long bytes[, factors]);
因素
是我们需要考虑的其他因素,我在上面提到过,对于每个应用程序都会有
predicted_time predict(long bytes[, factors]);
private void scheduleTask() {
while(WorkEvent()) {
while(!this.queue.isEmpty()) {
Task t = this.queue.poll();
for (int i = 0; i < this.maxNodes; i++) {
long predicted_time = this.nodes[i].predict(t);
if (predicted_time < 0) {
boolean b = this.queue.offer(t);
assert(b);
break;
}
if (predicted_time <= USER_EXPERIENCE_DELAY) {
this.nodes[i].addTask(t);
break;
}
alert_user(boolean b = this.queue.offer(t);
assert(b);
}
}
}
}
ReferenceQueue<Object> q = new ReferenceQueue<>();
SoftReference<Object> reference = new SoftReference<>(new Object(), q);
q.remove();
// reference removed - stop data load immediately