I'm using the serialized approach with an ergonomics approach to heap management. protobuf really sucks for a lot of reasons which are easily remediable if one is a protobuf maintainer, but I'm not and I don't care to be, and I don't have the time or inclination to write a new serializer.

However, and to Gil's point, following the ergonomics approach, when I resize an array, I drop a garbage-collectable array which I no longer "own", so from the PoV of the memory management code, it forms part of the "other" part of the heap, i.e. I have to subtract it from the total memory before computing the proportion of the heap I'm allowed to use. Over time, given that GC isn't timely, I can lose all my heap to these collectable arrays, and if I follow the rule about "Never call System.gc()" this actually isn't a very efficient approach either.

The next best approach is to allocate all my memory in 1Mb blocks, make each memory "region" be backed by some list of these blocks, so I never actually hand them to the GC, and just reduce my overall memory usage by dropping blocks, rather than totally resizing byte arrays.

My main disbelief is that I'm inventing all of this from scratch. It HAS to have been done before, right? All I want to do is sort a load of objects...!

S.

The presumed solution to this

On 11/10/18 8:51 AM, Gil Tene wrote:


On Friday, November 9, 2018 at 7:08:23 AM UTC-8, Shevek wrote:

    Hi,

    I'm trying to sort/merge a very large number of objects in Java, and
    failing more spectacularly than normal. The way I'm doing it is this:

    * Read a bunch of objects into an array.
    * Sort the array, then merge neighbouring objects as appropriate.
    * Re-fill the array, re-sort, re-merge until compaction is "not very
    successful".
    * Dump the array to file, repeat for next array.
    * Then stream all files through a final merge/combine phase.

    This is failing largely because I have no idea how large to make the
    array. Estimating the ongoing size using something like JAMM is too
    slow, and my hand-rolled memory estimator is too unreliable.

    The thing that seems to be working best is messing around with the
    array
    size in order to keep some concept of runtime.maxMemory() -
    runtime.totalMemory() + runtime.freeMemory() within a useful bound.

    But there must be a better solution. I can't quite think a way around
    this with SoftReference because I need to dump the data to disk when
    the
    reference gets broken, and defeating me right now.

    Other alternatives would include keeping all my in-memory data
    structures in serialized form, and paying the ser/deser cost to
    compare,
    but that's expensive - my main overhead right now is gc. Serialization
    is protobuf, although that's changeable, since it's annoying the hell
    out of me (please don't say thrift - but protobuf appears to have no
    way
    to read from a stream into a reusable object - it has to allocate the
    world every single time).


In general, whenever I see "my overhead is gc" and "unknown memory size" together, I see it as a sign of someone pushing heap utilization high and getting into the inefficient GC state. Simplistically, you should be able to drop the GC cost to an arbitrary % of overall computation cost by increasing the amount (or relative portion) of empty heap in your set. So GC should never be "a bottleneck" from a throughput point of view unless you have constraints (such as a minimum required live set and a maximum possible heap size) that force you towards a high utilization of the heap (in terms of LiveSet/HeapSize). The answer to such a situation is generally "get some more RAM for this problem" rather than put in tons of work to fit this in".

For most GC algorithms, or at least for the more costly parts of such algorithms, GC efficiency is roughly linear to EmptyHeap/LiveSet. Stated otherwise, GC cost grows with LiveSet/EmptyHeap or LiveSet/(HeapSize-LiveSet). As you grow the amount you try to cram into a heap of a given size, you increase the GC cost to the square of your cramming efforts. And for every doubling of the empty heap [for a given live set] you will generally half the GC cost.

This should [hopefully] make it obvious why using SoftReferences is a generally terrible idea.


    Issues:
    * This routine is not the sole tenant of the JVM. Other things use RAM.

You can try to establish what an "efficient enough" heap utilization level is for your use case (a level that keeps overall GC work as a % of CPU spend to e.g. below 10%), and keep your heap use to a related fraction of whatever heap size you get to have on the system you land on.

    * This has to be deployed and work on systems whose memory config is
    unknown to me.

    Can anybody please give me pointers?

    S.

--
You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected] <mailto:[email protected]>.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to