繁体   English   中英

Java:为什么使用固定数量的内存? 或它如何管理内存?

[英]Java: why does it uses a fixed amount of memory? or how does it manage the memory?

看来JVM使用了一些固定数量的内存。 至少我经常看到参数-Xmx (用于最大大小)和-Xms (用于初始大小)表明了这一点。

我感到Java应用程序不能很好地处理内存。 我注意到了一些事情:

  • 即使是一些非常小的示例演示应用程序也会加载大量的内存。 也许是由于Java库已加载。 但是为什么需要为每个Java实例加载库? (之所以这样,是因为多个小型应用程序线性地占用了更多的内存。有关我描述此问题的详细信息,请参见此处 。)或者为什么这样做呢?

  • 像Eclipse这样的大型Java应用程序经常会因某些OutOfMemory异常而崩溃。 这总是很奇怪,因为我的系统上仍然有足够的可用内存。 通常,它们在运行时会消耗越来越多的内存。 我不确定它们是否存在某些内存泄漏,或者是由于内存池中的碎片造成的,我感觉后者就是这种情况。

  • 与类似Qt之类的功能强大的库相比,Java库似乎需要更多的内存。 为什么是这样? (作为比较,启动一些Qt应用程序,查看它们的内存使用情况,然后启动一些Java应用程序。)

为什么它不只使用诸如mallocfree类的基础系统技术? 或者,如果他们不喜欢libc实现,则可以使用jemalloc (例如在FreeBSD和Firefox中 ),这似乎非常不错。 我非常确定这将比JVM内存池更好。 而且,不仅性能更好,而且需要更少的内存,尤其是。 适用于小型应用。


另外:有人已经尝试过了吗? 我会对基于LLVM的Java JIT编译器非常感兴趣,该编译器仅使用malloc / free进行内存处理。

也许这也因JVM实现而有所不同? 我主要使用Sun JVM。

(还要注意:我在这里不是直接谈论GC。GC仅负责计算可以删除哪些对象并初始化内存释放,但实际的释放是一个不同的子系统。Afaik,它是一些自己的内存池实施,而不仅仅是致电free 。)


编辑:一个非常相关的问题: (Sun)JVM为什么对内存使用量有固定的上限? 或换句话说:为什么JVM处理内存分配的方式与本机应用程序不同?

您需要记住,垃圾收集器所做的不仅是收集无法访问的对象。 它还优化了堆空间,并准确跟踪了用于创建新对象的内存确切位置

立即知道哪里有可用内存,可以高效地将新对象分配给年轻一代,并避免了往返于底层OS的需求。 Sun的Jon Masamitsu表示,JIT编译器还在JVM层之外优化了此类分配:

快速路径分配不会调用JVM来分配对象。 JIT编译器知道如何从年轻代中进行分配,并且为对象分配内联生成用于分配的代码。 解释器还知道如何在不调用VM的情况下进行分配。

请注意,JVM也竭尽全力尝试获取大的连续内存块,这可能有其自身的性能优势 (请参阅“丢失缓存的代价”)。 我想象对malloc (或替代方案)的调用在malloc调用之间提供连续内存的可能性很小,但也许我错过了一些东西。

此外,通过维护内存本身,垃圾收集器可以根据使用情况和访问模式进行分配优化。 现在,我不知道它在多大程度上做到了这一点,但是鉴于该概念已经获得了Sun的注册专利 ,我想他们已经做了一些事情。

保持分配这些内存块也为Java程序提供了保障。 由于垃圾回收对程序员来说是隐藏的,因此他们无法告诉JVM“不,请保留该内存;我已经完成了这些对象,但是我需要用于新对象的空间。” 通过保留内存,GC不会放弃无法恢复的内存。 自然地,您总是可以通过任何一种方式获得OutOfMemoryException ,但是似乎在每次处理完一个对象时都不必要地将内存归还给操作系统是更合理的选择,因为您已经麻烦自己获取了。

除了所有这些,我将尝试直接解决您的一些评论:

通常,它们在运行时会消耗越来越多的内存。

假设这不只是程序正在执行的操作(出于某种原因,也许有泄漏,也许它必须跟踪越来越多的数据),我想这与空闲哈希空间比率有关(Sun / Oracle)JVM设置的默认值。 -XX:MinHeapFreeRatio的默认值为40%,而-XX:MaxHeapFreeRatio的默认值为70%。 这意味着只要剩余40%的堆空间,堆就会通过从操作系统中声明更多的内存来重新调整大小(前提是这不会超过-Xmx )。 相反,如果可用空间超过70%,它将仅*将堆内存释放回操作系统。

考虑一下如果我在Eclipse中运行内存密集型操作会怎样? 例如,分析。 我的内存消耗将激增,并在整个过程中调整堆大小(可能多次)。 一旦完成,内存需求就会回落,但不会下降到足以使70%的堆可用。 这意味着现在分配了许多未充分利用的空间,JVM不想释放这些空间。 这是一个主要缺点,但是您可以通过根据情况自定义百分比来解决此问题。 为了更好地了解这一点,您确实应该对应用程序进行概要分析,以便可以查看已利用堆空间与已分配堆空间。 我个人使用YourKit ,但是有很多不错的选择。

*我不知道这是否真的是唯一的时间,以及从OS的角度来看如何观察到,但是文档说这是“ GC之后避免收缩的最大堆可用百分比”,这似乎暗示了这一点。

即使是一些非常小的示例演示应用程序也会加载大量的内存。

我猜这取决于它们是哪种应用程序。 我觉得Java GUI应用程序运行着大量内存,但是我没有任何证据可以证明。 您是否有我们可以看的具体示例?

但是为什么需要为每个Java实例加载库?

好吧,如果不创建新的JVM进程,您将如何处理多个Java应用程序的加载? 隔离进程是一件好事,这意味着可以独立加载。 不过,我认为对于一般流程而言,这并不罕见。

最后一点,您在另一个问题中询问的缓慢启动时间可能来自达到基线应用程序内存需求所必需的几个初始堆重新分配(由于-Xms-XX:MinHeapFreeRatio ),具体取决于默认值是您的JVM。

Java在虚拟机内部运行,这限制了其行为的许多部分。 注意术语“虚拟机”。 从字面上看,它好像机器是一个单独的实体一样在运行,而基础机器/ OS只是资源。 -Xmx值定义VM将拥有的最大内存量,而-Xms值定义应用程序可用的起始内存。

VM是二进制文件的一部分,是系统不可知的-这是一种用于允许字节码在任何地方执行的解决方案。 这类似于模拟器-对于旧游戏系统而言。 它模拟了运行游戏的“机器”。

遇到OutOfMemoryException的原因是因为虚拟机已达到-Xmx限制-它实际上已经用完了内存。

就较小的程序而言,它们通常需要更大比例的内存用于VM。 而且,Java具有默认的开始-Xmx和-Xms(我现在不记得它们的含义),它将始终以它开头。 当您开始构建和运行“实际”应用程序时,VM和库的开销变得不那么明显了。

与QT等有关的内存参数是正确的,但并非全部。 尽管它使用的内存比其中的更多,但这些内存是为特定体系结构编译的。 自从我使用QT或类似的库以来已经有一段时间了,但是我记得内存管理不是很健壮,并且内存泄漏在C / C ++程序中仍然很普遍。 垃圾回收的好处在于,它消除了许多导致内存泄漏的常见“陷阱”。 (注意:并非全部。在Java中仍然有可能泄漏内存,只是有点困难)。

希望这有助于消除您可能一直遇到的困惑。

回答部分问题;

Java在启动时会分配“堆”内存或固定大小的块(-Xms参数)。 它实际上并没有立即使用所有这些内存,但是它告诉操作系统“我想要这么多的内存”。 然后,当您创建对象并在Java环境中工作时,它将创建的对象放入此预分配的内存堆中。 如果该内存块已满,则它将向操作系统请求更多的内存,直到达到“最大堆大小”(-Xmx参数)为止。

一旦达到最大大小,即使有很多可用空间,Java也将不再从操作系统请求更多RAM。 如果尝试创建更多对象,则将没有堆空间,并且将收到OutOfMemory异常。 现在,如果您正在查看Windows Task Manager或类似的东西,您将看到使用X内存的“ java.exe”。 这种排序对应于它为堆请求的内存量,而不是实际使用的堆中的内存量。

换句话说,我可以编写应用程序:

class myfirstjavaprog
{  
    public static void main(String args[])
    {
       System.out.println("Hello World!");
    }
}

这基本上将占用很少的内存。 但是,如果我使用cmd行运行它:

java.exe myfirstjavaprog -Xms 1024M

然后在启动时,java会立即向操作系统要求1,024 MB的内存,这就是Windows Task Manager中将显示的内容。 实际上,不使用该ram,但是java保留了该ram供以后使用。

相反,如果我有一个尝试创建10,000字节大数组的应用程序:

class myfirstjavaprog
{  
    public static void main(String args[])
    {
       byte[] myArray = new byte[10000];
    }
}

但使用命令行运行它:

java.exe myfirstjavaprog -Xms 100 -Xmx 100

这样,Java最多只能分配100个字节的内存。 由于10,000字节的数组无法放入100字节的堆中,因此即使操作系统具有大量RAM,也会抛出OutOfMemory异常。

我希望这是有道理的...


编辑:

回到“为什么Java使用这么多内存”; 您为什么认为它占用大量内存? 如果您正在查看操作系统报告的内容,那么这并不是其实际使用的内容,而是仅保留使用的内容。 如果您想知道Java实际使用了什么,则可以进行堆转储并浏览堆中的每个对象,并查看其使用了多少内存。

要回答“为什么不让OS处理呢?”,我想对于那些设计它的人来说,这只是一个基本的Java问题。 我的看法 Java在JVM(虚拟机)中运行。 如果创建VMWare实例或系统的几乎任何其他“虚拟化”,通常必须指定虚拟系统将/将消耗多少内存。 我认为JVM是相似的。 同样,这种抽象的内存模型使不同操作系统的JVM都以类似的方式起作用。 因此,例如,Linux和Windows具有不同的RAM分配模型,但是JVM可以将其抽象化并遵循不同操作系统的相同内存使用情况。

Java确实使用了mallocfree ,或者至少使用了JVM的实现。 但是,由于Java跟踪分配并且垃圾收集了无法访问的对象,所以绝对不够。

至于其余的文字,我不确定那里是否有问题。

即使是一些非常小的示例演示应用程序也会加载大量的内存。 也许是由于Java库已加载。 但是为什么需要为每个Java实例加载库? (之所以这样,是因为多个小型应用程序线性地占用了更多的内存。有关我描述此问题的详细信息,请参见此处。)或者为什么这样做呢?

这可能是由于启动和运行JVM的开销所致

像Eclipse这样的大型Java应用程序经常会因某些OutOfMemory异常而崩溃。 这总是很奇怪,因为我的系统上仍然有足够的可用内存。 通常,它们在运行时会消耗越来越多的内存。 我不确定它们是否存在某些内存泄漏,或者是由于内存池中的碎片造成的,我感觉后者就是这种情况。

我不确定您所说的“经常崩溃”是什么意思,因为我认为这种情况在很长一段时间内都没有发生过。 如果是这样,则可能是由于您之前提到的“最大尺寸”设置所致。

您的主要问题是,为什么Java不使用mallocfree取决于目标市场。 Java旨在消除开发人员的内存管理难题。 Java的垃圾回收器在可以释放内存时在释放内存方面做得相当不错,但是在内存受限的情况下,Java并不打算与C ++竞争。 Java很好地完成了预期的工作(删除了开发人员级别的内存管理),并且JVM很好地承担了责任,以至于对于大多数应用程序来说已经足够好了。

这些限制是Sun故意设计的决定。 我已经看到至少另外两个没有这种设计的JVM-Microsoft的和IBM的非pc AS / 400系统。 两者都根据需要使用所需的更多内存来增长。

Java不使用固定大小的内存,而是始终在-Xms到-Xmx的范围内。

如果Eclipse因OutOfMemoryError而崩溃,则它所需的内存将超过-Xmx所授予的内存(配置问题)。

Java不得使用malloc / free(用于创建对象),因为由于垃圾回收(GC),其内存处理方式有很大不同。 GC自动删除未使用的对象,与负责内存管理相比,这是一个好处。

有关此复杂主题的详细信息,请参见优化垃圾收集。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM