简体   繁体   English

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

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

It seems that the JVM uses some fixed amount of memory. 看来JVM使用了一些固定数量的内存。 At least I have often seen parameters -Xmx (for the maximum size) and -Xms (for the initial size) which suggest that. 至少我经常看到参数-Xmx (用于最大大小)和-Xms (用于初始大小)表明了这一点。

I got the feeling that Java applications don't handle memory very well. 我感到Java应用程序不能很好地处理内存。 Some things I have noticed: 我注意到了一些事情:

  • Even some very small sample demo applications load huge amounts of memory. 即使是一些非常小的示例演示应用程序也会加载大量的内存。 Maybe this is because of the Java library which is loaded. 也许是由于Java库已加载。 But why is it needed to load the library for each Java instance? 但是为什么需要为每个Java实例加载库? (It seems that way because multiple small applications linearly take more memory. See here for some details where I describe this problem.) Or why is it done that way? (之所以这样,是因为多个小型应用程序线性地占用了更多的内存。有关我描述此问题的详细信息,请参见此处 。)或者为什么这样做呢?

  • Big Java applications like Eclipse often crash with some OutOfMemory exception. 像Eclipse这样的大型Java应用程序经常会因某些OutOfMemory异常而崩溃。 This was always strange because there was still plenty of memory available on my system. 这总是很奇怪,因为我的系统上仍然有足够的可用内存。 Often, they consume more and more memory over runtime. 通常,它们在运行时会消耗越来越多的内存。 I'm not sure if they have some memory leaks or if this is because of fragmentation in the memory pool -- I got the feeling that the latter is the case. 我不确定它们是否存在某些内存泄漏,或者是由于内存池中的碎片造成的,我感觉后者就是这种情况。

  • The Java library seem to require much more memory than similar powerful libraries like Qt for example. 与类似Qt之类的功能强大的库相比,Java库似乎需要更多的内存。 Why is this? 为什么是这样? (To compare, start some Qt applications and look at their memory usage and start some Java apps.) (作为比较,启动一些Qt应用程序,查看它们的内存使用情况,然后启动一些Java应用程序。)

Why doesn't it use just the underlying system technics like malloc and free ? 为什么它不只使用诸如mallocfree类的基础系统技术? Or if they don't like the libc implementation, they could use jemalloc (like in FreeBSD and Firefox ) which seems to be quite good. 或者,如果他们不喜欢libc实现,则可以使用jemalloc (例如在FreeBSD和Firefox中 ),这似乎非常不错。 I am quite sure that this would perform better than the JVM memory pool. 我非常确定这将比JVM内存池更好。 And not only perform better, also require less memory, esp. 而且,不仅性能更好,而且需要更少的内存,尤其是。 for small applications. 适用于小型应用。


Addition: Does somebody have tried that already? 另外:有人已经尝试过了吗? I would be much interested in a LLVM based JIT-compiler for Java which just uses malloc / free for memory handling. 我会对基于LLVM的Java JIT编译器非常感兴趣,该编译器仅使用malloc / free进行内存处理。

Or maybe this also differs from JVM implementation to implementation? 也许这也因JVM实现而有所不同? I have used mostly the Sun JVM. 我主要使用Sun JVM。

(Also note: I'm not directly speaking about the GC here. The GC is only responsible to calculate what objects can be deleted and to initialize the memory freeing but the actual freeing is a different subsystem. Afaik, it is some own memory pool implementation, not just a call to free .) (还要注意:我在这里不是直接谈论GC。GC仅负责计算可以删除哪些对象并初始化内存释放,但实际的释放是一个不同的子系统。Afaik,它是一些自己的内存池实施,而不仅仅是致电free 。)


Edit: A very related question: Why does the (Sun) JVM have a fixed upper limit for memory usage? 编辑:一个非常相关的问题: (Sun)JVM为什么对内存使用量有固定的上限? Or to put it differently: Why does JVM handle memory allocations differently than native applications? 或换句话说:为什么JVM处理内存分配的方式与本机应用程序不同?

You need to keep in mind that the Garbage Collector does a lot more than just collecting unreachable objects. 您需要记住,垃圾收集器所做的不仅是收集无法访问的对象。 It also optimizes the heap space and keeps track of exactly where there is memory available to allocate for the creation of new objects. 它还优化了堆空间,并准确跟踪了用于创建新对象的内存确切位置

Knowing immediately where there is free memory makes the allocation of new objects into the young generation efficient, and prevents the need to run back and forth to the underlying OS. 立即知道哪里有可用内存,可以高效地将新对象分配给年轻一代,并避免了往返于底层OS的需求。 The JIT compiler also optimizes such allocations away from the JVM layer, according to Sun's Jon Masamitsu: Sun的Jon Masamitsu表示,JIT编译器还在JVM层之外优化了此类分配:

Fast-path allocation does not call into the JVM to allocate an object. 快速路径分配不会调用JVM来分配对象。 The JIT compilers know how to allocate out of the young generation and code for an allocation is generated in-line for object allocation. JIT编译器知道如何从年轻代中进行分配,并且为对象分配内联生成用于分配的代码。 The interpreter also knows how to do the allocation without making a call to the VM. 解释器还知道如何在不调用VM的情况下进行分配。

Note that the JVM goes to great lengths to try to get large contiguous memory blocks as well, which likely have their own performance benefits (See "The Cost of Missing the Cache"). 请注意,JVM也竭尽全力尝试获取大的连续内存块,这可能有其自身的性能优势 (请参阅“丢失缓存的代价”)。 I imagine calls to malloc (or the alternatives) have a limited likelihood of providing contiguous memory across calls, but maybe I missed something there. 我想象对malloc (或替代方案)的调用在malloc调用之间提供连续内存的可能性很小,但也许我错过了一些东西。

Additionally, by maintaining the memory itself, the Garbage Collector can make allocation optimizations based on usage and access patterns. 此外,通过维护内存本身,垃圾收集器可以根据使用情况和访问模式进行分配优化。 Now, I have no idea to what extent it does this, but given that there's a registered Sun patent for this concept , I imagine they've done something with it. 现在,我不知道它在多大程度上做到了这一点,但是鉴于该概念已经获得了Sun的注册专利 ,我想他们已经做了一些事情。

Keeping these memory blocks allocated also provides a safeguard for the Java program. 保持分配这些内存块也为Java程序提供了保障。 Since the garbage collection is hidden from the programmer, they can't tell the JVM "No, keep that memory; I'm done with these objects, but I'll need the space for new ones." 由于垃圾回收对程序员来说是隐藏的,因此他们无法告诉JVM“不,请保留该内存;我已经完成了这些对象,但是我需要用于新对象的空间。” By keeping the memory, the GC doesn't risk giving up memory it won't be able to get back. 通过保留内存,GC不会放弃无法恢复的内存。 Naturally, you can always get an OutOfMemoryException either way, but it seems more reasonable not to needlessly give memory back to the operating system every time you're done with an object, since you already went to the trouble to get it for yourself. 自然地,您总是可以通过任何一种方式获得OutOfMemoryException ,但是似乎在每次处理完一个对象时都不必要地将内存归还给操作系统是更合理的选择,因为您已经麻烦自己获取了。

All of that aside, I'll try to directly address a few of your comments: 除了所有这些,我将尝试直接解决您的一些评论:

Often, they consume more and more memory over runtime. 通常,它们在运行时会消耗越来越多的内存。

Assuming that this isn't just what the program is doing (for whatever reason, maybe it has a leak, maybe it has to keep track of an increasing amount of data), I imagine that it has to do with the free hash space ratio defaults set by the (Sun/Oracle) JVM. 假设这不只是程序正在执行的操作(出于某种原因,也许有泄漏,也许它必须跟踪越来越多的数据),我想这与空闲哈希空间比率有关(Sun / Oracle)JVM设置的默认值。 The default value for -XX:MinHeapFreeRatio is 40%, while -XX:MaxHeapFreeRatio is 70%. -XX:MinHeapFreeRatio的默认值为40%,而-XX:MaxHeapFreeRatio的默认值为70%。 This means that any time there is only 40% of the heap space remaining, the heap will be resized by claiming more memory from the operating system (provided that this won't exceed -Xmx ). 这意味着只要剩余40%的堆空间,堆就会通过从操作系统中声明更多的内存来重新调整大小(前提是这不会超过-Xmx )。 Conversely, it will only* free heap memory back to the operating system if the free space exceeds 70%. 相反,如果可用空间超过70%,它将仅*将堆内存释放回操作系统。

Consider what happens if I run a memory-intensive operation in Eclipse; 考虑一下如果我在Eclipse中运行内存密集型操作会怎样? profiling, for example. 例如,分析。 My memory consumption will shoot up, resizing the heap (likely multiple times) along the way. 我的内存消耗将激增,并在整个过程中调整堆大小(可能多次)。 Once I'm done, the memory requirement falls back down, but it likely won't drop so far that 70% of the heap is free. 一旦完成,内存需求就会回落,但不会下降到足以使70%的堆可用。 That means that there's now a lot of underutilized space allocated that the JVM has no intention of releasing. 这意味着现在分配了许多未充分利用的空间,JVM不想释放这些空间。 This is a major drawback, but you might be able to work around it by customizing the percentages to your situation. 这是一个主要缺点,但是您可以通过根据情况自定义百分比来解决此问题。 To get a better picture of this, you really should profile your application so you can see the utilized versus allocated heap space. 为了更好地了解这一点,您确实应该对应用程序进行概要分析,以便可以查看已利用堆空间与已分配堆空间。 I personally use YourKit , but there are many good alternatives to choose from. 我个人使用YourKit ,但是有很多不错的选择。

*I don't know if this is actually the only time and how this is observed from the perspective of the OS, but the documentation says it's the "maximum percentage of heap free after GC to avoid shrinking ," which seems to suggest that. *我不知道这是否真的是唯一的时间,以及从OS的角度来看如何观察到,但是文档说这是“ GC之后避免收缩的最大堆可用百分比”,这似乎暗示了这一点。

Even some very small sample demo applications load huge amounts of memory. 即使是一些非常小的示例演示应用程序也会加载大量的内存。

I guess this depends on what kind of applications they are. 我猜这取决于它们是哪种应用程序。 I feel that Java GUI applications run memory-heavy, but I don't have any evidence one way or another. 我觉得Java GUI应用程序运行着大量内存,但是我没有任何证据可以证明。 Did you have a specific example that we could look at? 您是否有我们可以看的具体示例?

But why is it needed to load the library for each Java instance? 但是为什么需要为每个Java实例加载库?

Well, how would you handle loading multiple Java applications if not creating new JVM processes? 好吧,如果不创建新的JVM进程,您将如何处理多个Java应用程序的加载? The isolation of the processes is a good thing, which means independent loading. 隔离进程是一件好事,这意味着可以独立加载。 I don't think that's so uncommon for processes in general, though. 不过,我认为对于一般流程而言,这并不罕见。

As a final note, the slow start times you asked about in another question likely come from several intial heap reallocations necessary to get to the baseline application memory requirement (due to -Xms and -XX:MinHeapFreeRatio ), depending what the default values are with your JVM. 最后一点,您在另一个问题中询问的缓慢启动时间可能来自达到基线应用程序内存需求所必需的几个初始堆重新分配(由于-Xms-XX:MinHeapFreeRatio ),具体取决于默认值是您的JVM。

Java runs inside a Virtual Machine, which constrains many parts of its behavior. Java在虚拟机内部运行,这限制了其行为的许多部分。 Note the term "Virtual Machine." 注意术语“虚拟机”。 It is literally running as though the machine is a separate entity, and the underlying machine/OS are simply resources. 从字面上看,它好像机器是一个单独的实体一样在运行,而基础机器/ OS只是资源。 The -Xmx value is defining the maximum amount of memory that the VM will have, while the -Xms defines the starting memory available to the application. -Xmx值定义VM将拥有的最大内存量,而-Xms值定义应用程序可用的起始内存。

The VM is a product of the binary being system agnostic - this was a solution used to allow the byte code to execute wherever. VM是二进制文件的一部分,是系统不可知的-这是一种用于允许字节码在任何地方执行的解决方案。 This is similar to an emulator - say for old gaming systems. 这类似于模拟器-对于旧游戏系统而言。 It is emulating the "machine" that the game runs on. 它模拟了运行游戏的“机器”。

The reason why you run into an OutOfMemoryException is because the Virtual Machine has hit the -Xmx limit - it has literally run out of memory. 遇到OutOfMemoryException的原因是因为虚拟机已达到-Xmx限制-它实际上已经用完了内存。

As far as smaller programs go, they will often require a larger percentage of their memory for the VM. 就较小的程序而言,它们通常需要更大比例的内存用于VM。 Also, Java has a default starting -Xmx and -Xms (I don't remember what they are right now) that it will always start with. 而且,Java具有默认的开始-Xmx和-Xms(我现在不记得它们的含义),它将始终以它开头。 The overhead of the VM and the libraries becomes much less noticable when you start to build and run "real" applications. 当您开始构建和运行“实际”应用程序时,VM和库的开销变得不那么明显了。

The memory argument related to QT and the like is true, but is not the whole story. 与QT等有关的内存参数是正确的,但并非全部。 While it uses more memory than some of those, those are compiled for specific architectures. 尽管它使用的内存比其中的更多,但这些内存是为特定体系结构编译的。 It has been a while since I have used QT or similar libraries, but I remember the memory management not being very robust, and memory leaks are still common today in C/C++ programs. 自从我使用QT或类似的库以来已经有一段时间了,但是我记得内存管理不是很健壮,并且内存泄漏在C / C ++程序中仍然很普遍。 The nice thing about Garbage Collection is that it removes many of the common "gotchas" that cause memory leaks. 垃圾回收的好处在于,它消除了许多导致内存泄漏的常见“陷阱”。 (Note: Not all of them. It is still very possible to leak memory in Java, just a bit harder). (注意:并非全部。在Java中仍然有可能泄漏内存,只是有点困难)。

Hope this helps clear up some of the confusion you may have been having. 希望这有助于消除您可能一直遇到的困惑。

To answer a portion of your question; 回答部分问题;

Java at start-up allocates a "heap" of memory, or a fixed size block (the -Xms parameter). Java在启动时会分配“堆”内存或固定大小的块(-Xms参数)。 It doesn't actually use all this memory right off the bat, but it tells the OS "I want this much memory". 它实际上并没有立即使用所有这些内存,但是它告诉操作系统“我想要这么多的内存”。 Then as you create objects and do work in the Java environment, it puts the created objects into this heap of pre-allocated memory. 然后,当您创建对象并在Java环境中工作时,它将创建的对象放入此预分配的内存堆中。 If that block of memory gets full then it will request a little more memory from the OS, up until the "max heap size" (the -Xmx parameter) is reached. 如果该内存块已满,则它将向操作系统请求更多的内存,直到达到“最大堆大小”(-Xmx参数)为止。

Once that max size is reached, Java will no longer request more RAM from the OS, even if there is a lot free. 一旦达到最大大小,即使有很多可用空间,Java也将不再从操作系统请求更多RAM。 If you try to create more objects, there is no heap space left, and you will get an OutOfMemory exception. 如果尝试创建更多对象,则将没有堆空间,并且将收到OutOfMemory异常。 Now if you are looking at Windows Task Manager or something like that, you'll see "java.exe" using X megs of memory. 现在,如果您正在查看Windows Task Manager或类似的东西,您将看到使用X内存的“ java.exe”。 That sort-of corresponds to the amount of memory that it has requested for the heap, not really the amount of memory inside the heap thats used. 这种排序对应于它为堆请求的内存量,而不是实际使用的堆中的内存量。

In other words, I could write the application: 换句话说,我可以编写应用程序:

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

Which would basically take very little memory. 这基本上将占用很少的内存。 But if I ran it with the cmd line: 但是,如果我使用cmd行运行它:

java.exe myfirstjavaprog -Xms 1024M

then on startup java will immediately ask the OS for 1,024 MB of ram, and thats what will show in Windows Task Manager. 然后在启动时,java会立即向操作系统要求1,024 MB的内存,这就是Windows Task Manager中将显示的内容。 In actuallity, that ram isnt being used, but java reserved it for later use. 实际上,不使用该ram,但是java保留了该ram供以后使用。

Conversely, if I had an app that tried to create a 10,000 byte large array: 相反,如果我有一个尝试创建10,000字节大数组的应用程序:

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

but ran it with the command line: 但使用命令行运行它:

java.exe myfirstjavaprog -Xms 100 -Xmx 100

Then Java could only alocate up to 100 bytes of memory. 这样,Java最多只能分配100个字节的内存。 Since a 10,000 byte array won't fit into a 100 byte heap, that would throw an OutOfMemory exception, even though the OS has plenty of RAM. 由于10,000字节的数组无法放入100字节的堆中,因此即使操作系统具有大量RAM,也会抛出OutOfMemory异常。

I hope that makes sense... 我希望这是有道理的...


Edit: 编辑:

Going back to "why Java uses so much memory"; 回到“为什么Java使用这么多内存”; why do you think its using a lot of memory? 您为什么认为它占用大量内存? If you are looking at what the OS reports, then that isn't what its actually using, its only what its reserved for use. 如果您正在查看操作系统报告的内容,那么这并不是其实际使用的内容,而是仅保留使用的内容。 If you want to know what java has actually used, then you can do a heap dump and explore every object in the heap and see how much memory its using. 如果您想知道Java实际使用了什么,则可以进行堆转储并浏览堆中的每个对象,并查看其使用了多少内存。

To answer "why doesn't it just let the OS handle it?", well I guess that is just a fundamental Java question for those that designed it. 要回答“为什么不让OS处理呢?”,我想对于那些设计它的人来说,这只是一个基本的Java问题。 The way I look at it; 我的看法 Java runs in the JVM, which is a virtual machine. Java在JVM(虚拟机)中运行。 If you create a VMWare instance or just about any other "virtualization" of a system, you usually have to specify how much memory that virtual system will/can consume. 如果创建VMWare实例或系统的几乎任何其他“虚拟化”,通常必须指定虚拟系统将/将消耗多少内存。 I consider the JVM to be similar. 我认为JVM是相似的。 Also, this abstracted memory model lets the JVM's for different OSes all act in a similar way. 同样,这种抽象的内存模型使不同操作系统的JVM都以类似的方式起作用。 So for example Linux and Windows have different RAM allocation models, but the JVM can abstract that away and follow the same memory usage for the different OSes. 因此,例如,Linux和Windows具有不同的RAM分配模型,但是JVM可以将其抽象化并遵循不同操作系统的相同内存使用情况。

Java does use malloc and free , or at least the implementations of the JVM may. Java确实使用了mallocfree ,或者至少使用了JVM的实现。 But since Java tracks allocations and garbage collects unreachable objects, they are definitely not enough. 但是,由于Java跟踪分配并且垃圾收集了无法访问的对象,所以绝对不够。

As for the rest of your text, I'm not sure if there's a question there. 至于其余的文字,我不确定那里是否有问题。

Even some very small sample demo applications load huge amounts of memory. 即使是一些非常小的示例演示应用程序也会加载大量的内存。 Maybe this is because of the Java library which is loaded. 也许是由于Java库已加载。 But why is it needed to load the library for each Java instance? 但是为什么需要为每个Java实例加载库? (It seems that way because multiple small applications linearly take more memory. See here for some details where I describe this problem.) Or why is it done that way? (之所以这样,是因为多个小型应用程序线性地占用了更多的内存。有关我描述此问题的详细信息,请参见此处。)或者为什么这样做呢?

That's likely due to the overhead of starting and running the JVM 这可能是由于启动和运行JVM的开销所致

Big Java applications like Eclipse often crash with some OutOfMemory exception. 像Eclipse这样的大型Java应用程序经常会因某些OutOfMemory异常而崩溃。 This was always strange because there was still plenty of memory available on my system. 这总是很奇怪,因为我的系统上仍然有足够的可用内存。 Often, they consume more and more memory over runtime. 通常,它们在运行时会消耗越来越多的内存。 I'm not sure if they have some memory leaks or if this is because of fragmentation in the memory pool -- I got the feeling that the latter is the case. 我不确定它们是否存在某些内存泄漏,或者是由于内存池中的碎片造成的,我感觉后者就是这种情况。

I'm not entirely sure what you mean by "often crash," as I don't think this has happened to me in quite a long time. 我不确定您所说的“经常崩溃”是什么意思,因为我认为这种情况在很长一段时间内都没有发生过。 If it is, it's likely due to the "maximum size" setting you mentioned earlier. 如果是这样,则可能是由于您之前提到的“最大尺寸”设置所致。

Your main question asking why Java doesn't use malloc and free comes down to a matter of target market. 您的主要问题是,为什么Java不使用mallocfree取决于目标市场。 Java was designed to eliminate the headache of memory management from the developer. Java旨在消除开发人员的内存管理难题。 Java's garbage collector does a reasonably good job of freeing up memory when it can be freed, but Java isn't meant to rival C++ in situations with memory restrictions. Java的垃圾回收器在可以释放内存时在释放内存方面做得相当不错,但是在内存受限的情况下,Java并不打算与C ++竞争。 Java does what it was intended to do (remove developer level memory management) well, and the JVM picks up the responsibility well enough that it's good enough for most applications. Java很好地完成了预期的工作(删除了开发人员级别的内存管理),并且JVM很好地承担了责任,以至于对于大多数应用程序来说已经足够好了。

The limits are a deliberate design decision from Sun. 这些限制是Sun故意设计的决定。 I've seen at least two other JVM's which does not have this design - the Microsoft one and the IBM one for their non-pc AS/400 systems. 我已经看到至少另外两个没有这种设计的JVM-Microsoft的和IBM的非pc AS / 400系统。 Both grows as needed using as much memory as needed. 两者都根据需要使用所需的更多内存来增长。

Java doesn't use a fixed size of memory it is always in the range from -Xms to -Xmx. Java不使用固定大小的内存,而是始终在-Xms到-Xmx的范围内。

If Eclipse crashes with OutOfMemoryError, than it required more memory than granted by -Xmx (a coniguration issue). 如果Eclipse因OutOfMemoryError而崩溃,则它所需的内存将超过-Xmx所授予的内存(配置问题)。

Java must not use malloc/free (for object creation) since its memory handling is much different due to garbage collection (GC). Java不得使用malloc / free(用于创建对象),因为由于垃圾回收(GC),其内存处理方式有很大不同。 GC removes automatically unused objects, which is a benefit compared to be responsible for memory management. GC自动删除未使用的对象,与负责内存管理相比,这是一个好处。

For details on this complex topic see Tuning Garbage Collection 有关此复杂主题的详细信息,请参见优化垃圾收集。

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

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