I'm using float buffers as direct byte buffers as required for opengl drawing in Android. The problem that is that upon creating the byte buffer, the GC goes crazy---as in 30s+ crazy. I'm creating a mesh of 40x40 vertices, or 1600 vertices, or 4800 floats. As per the profiler, the culprit that calls the GC is ByteBuffer.allocateDirect.
Is this normal or expected for creating a mesh this size? It seems pretty tame.
The buffer init() code is below:
public static FloatBuffer createFloatBuffer(int capacity) {
ByteBuffer vbb = ByteBuffer.allocateDirect(capacity * 4);
vbb.order(ByteOrder.nativeOrder());
return vbb.asFloatBuffer();
}
Your question says allocateDirect, but your code says allocate. Which are you using?
allocateDirect is known to call System.gc in an attempt to force DirectByteBuffer to be reclaimed before trying (and failing) to allocate a new direct byte buffer.
See this answer for one suggestion on avoiding the GC. Alternatively, you could try creating a pool of appropriately-sized DirectByteBuffer rather than continuously creating new ones.
Related
I'm writing some stuff that uses ByteBuffers. In the docs of the API it says
There is no way to free a buffer explicitly (without JVM specific
reflection). Buffer objects are subject to GC and it usually takes two
GC cycles to free the off-heap memory after the buffer object becomes
unreachable.
However in a SO post's accepted answer I read
BigMemory uses the memory address space of the JVM process, via direct
ByteBuffers that are not subject to GC unlike other native Java
objects.
Now what should I do, shall I free the created buffer? Or do I misunderstand something in the docs or the answer?
It depends how you create the buffer, there are many possible use cases. Regular ByteBuffer.allocate() will be created on the heap and will be collected by the GC. Other options e.g. native memory might not.
Terracotta BigMemory is a type of native off-heap memory which is not governed by the JVM GC. If you allocate a buffer in this type of memory you have to clear it yourself.
It might be a good idea to clear the buffer even if it's allocated in the heap memory. GC will take care of collecting unused buffer it but this will take some time.
As the documentation of the BufferUtils in LWJGL also say: There is no way to explicitly free a ByteBuffer.
The ByteBuffer objects that are allocated with the standard mechanism (namely, by directly or indirectly calling ByteBuffer#allocateDirect) are subject to GC, and will be cleaned up eventually.
The answer that you linked to seems to refer to the BigMemory library in particular. Using JNI, you can create a (direct) ByteBffer that is not handled by the GC, and where it is up to you to actually free the underlying data.
However, a short advice: When dealing with LWJGL and other libraries that rely on (direct) ByteBuffer objects for the data transfer to the native side, you should think about the usage pattern of these buffers. Particularly for OpenGL binding libraries, you'll frequently need a ByteBuffer that only has space for 16 float values, for example (e.g. containing a matrix that is sent to OpenGL). And in many cases, the methods that do the data transfer with these buffers will be called frequently.
In such a case, it is usually not a good idea to allocate these small, short-lived buffers repeatedly:
class Renderer {
void renderMethodThatIsCalledThousandsOfTimesPerSecond() {
ByteBuffer bb = ByteBuffer.allocateDirect(16 * 4);
fill(bb);
passToOpenGL(bb);
}
}
The creation of these buffers and the GC can significantly reduce performance - and distressingly in the form of GC pauses, that could cause lags in a game.
For such cases, it can be beneficial to pull out the allocation, and re-use the buffer:
class Renderer {
private final ByteBuffer MATRIX_BUFFER_4x4 = ByteBuffer.allocateDirect(16 * 4);
void renderMethodThatIsCalledThousandsOfTimesPerSecond() {
fill(MATRIX_BUFFER_4x4);
passToOpenGL(MATRIX_BUFFER_4x4);
}
}
The program is receiving the image data in bytes from IP camera and then process the image. The first time when the program starts uses 470Mb of RAM, and in every 1 second it increases up to 15Mb, it will continue till there is no enough space and the computer hanged up.
The method getImage() is called every 100ms
I have done some experiment going to share here. The original code is like this: (in which the buffer is created only once and after that, it can be reused)
private static final int WIDTH = 640;
private static final int HEIGHT = 480;
private byte[] sJpegPicBuffer = new byte[WIDTH * HEIGHT];
private Mat readImage() throws Exception {
boolean isGetSuccess = camera.getImage(lUserID, sJpegPicBuffer, WIDTH * HEIGHT);
if (isGetSuccess) {
return Imgcodecs.imdecode(new MatOfByte(sJpegPicBuffer), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
}
return null;
}
In the above code, RAM goes up to Computer hang up (99% 10Gb). Then I changed the code like this: (in every loop it will create a new buffer)
private static final int WIDTH = 640;
private static final int HEIGHT = 480;
private Mat readImage() throws Exception {
byte[] sJpegPicBuffer = new byte[WIDTH * HEIGHT];
boolean isGetSuccess = camera.getImage(lUserID, sJpegPicBuffer, WIDTH * HEIGHT);
if (isGetSuccess) {
return Imgcodecs.imdecode(new MatOfByte(sJpegPicBuffer), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
}
return null;
}
In this above code the RAM goes up to about 43% (5Gb)and then freed up.
Now the question is in the first block of code seems to be optimized, the buffer can be reused avoiding to create new space of memory in every call, but the result is not something we want. Why?
In the second block of code, it seems that the code is not as optimized as the first one, but works well than the first one.
But in general why the RAM increasing up to 10Gb in the first case and 5Gb in the second case. How can we control this situation?
This is a speculation, though I've seen similar scenario in real live few times.
Your Java code is interacting with native camera SDK (dll). Native code is like to allocating buffers in non JVM memory and use some internal Java objects to access that buffers. Common (a very poor) practice is to relay on Java object finalizer deallocate native buffer if it is not used any more.
Finalizers rely on garbage collector to trigger them, and this is a reason that pattern often fails. Although, finalizer is guarantied to run eventually, in practice it would not happen as long as there are enough space in Java heap and native memory would not be deallocated in timely fashion.
Java heap size have hard limit, but native memory pool used by C/C++ can grow as long as OS allow it to grow.
Concerning your problem
I assume in your first snippet, Java heap traffic is low. GC is idle and no finalizers are executes, thus memory allocated outside of Java heap keeps growing.
In second snippet, your are creating pressure on Java heap forcing GC to run frequently. As a side effect of GC finializer are executed and native memory released.
Instead of finalizers and buffer allocated in native code, your camera SDK may relay on Java direct memory buffers (these memory is direct accessing for C code so it is convenient to pass data over JVM boundary). Though effect would be mostly the same, because Java direct buffers implementation is using same pattern (with phantom references in stead of finalizers).
Suggestions
-XX:+PrintGCDetails and -XX:+PrintReferenceGC options would print information about reference processing so you can verify if finalizer/phantom references are indeed being used.
look at you camera's SDK docs to see is it is possible to release native resources early via API
option -XX:MaxDirectMemorySize=X can be used to cap direct buffer usage, if your camara's SDK relays on them. Though it is not a solution, but a safety net to let your application OOM before OS memory is exhausted
force every few frames (e.g. System.gc()). This is another poor option as behavior of System.gc() is JVM dependent.
PS
This is my post about resource management with finalizers and phantom references.
I'm trying parse a ByteBuf that is not in the JVM heap, to Google Protocol Buffer object. In fact it's the direct memory byte buffer which Netty passed to me.
This is what I am currently doing:
ByteBuf buf = ...;
ByteBufInputStream stream = new ByteBufInputStream(buf);
Message msg = MyPbMessage.getDefaultInstance().getParserForType().parseFrom(stream);
This can work. However, I found this type of parsing introduce new byte array per message, and cause a lot GC.
So is there a way to avoid these in heap byte arrays creating? i.e, parse Google Protocol Buffer bytes directly from native memory.
You can do it the way the Guava guys do store a small buffer (1024 bytes) in a ThreadLocal, use it if it suffices and never put a bigger buffer in the TL.
This'll work fine as long as most requests can be served by it. If the average/median size is too big, you could go for soft/weak reference, however, without some real testing it's hard to tell if it helps.
You could combine the approaches, i.e., use a strongly referenced small buffer and a weakly referenced big buffer in the TL. You could pool your buffers, you could...
... but note that it all has its dark side. Wasted memory, prolonged buffer lifetime leading to promoting them to the old generations, where garbage collecting is much more expensive.
Few days ago I was given a solution of checking collision between two bitmaps that has config_alpha_8. But upon using it I noticed my app started lagging oddly, and when I checked the logs I noticed the garbage collector was spamming every millisecond
I tried removing few lines, and found out what causing the garbage collector going hype sh*t were these lines:
byte[] pixelData = getPixels(bitmap1);
byte[] pixelData2 = getPixels(bitmap2);
which called this function:
public byte[] getPixels(Bitmap bmp) {
int bytes = bmp.getRowBytes() * bmp.getHeight();
ByteBuffer buffer = ByteBuffer.allocate(bytes);
bmp.copyPixelsToBuffer(buffer);
return buffer.array();
}
Why? What can I do to make it stop?
You are allocating large contiguous blocks of memory (i.e. a byte[]). Depending on how large your images are, this could be accounting for a significant amount of your available heap.
If you are going to be doing a lot of these type of operations, it may be worth considering pooling byte[] instances of fixed sizes to be reused.
I am having some memory problems with a project of mine. After a bit of tracing memory on several positions in my program, I retraced the problem to this line:
(FloatBuffer)lightBuffer.asFloatBuffer().put(lightAmbient).flip()
I am using the resultant buffer in a function straight away, but it seems that that float buffer is not emptied after it has been used.
So how do I properly empty/dereference a buffer in java?
ps: I tried the clear() method, but according to the java documentation that only resets the buffer; it does not remove the data from it.
Assuming your lightBuffer is a ByteBuffer (it seems no other class has a method asFloatBuffer), your FloatBuffer object is only a wrapper around the same underlying byte[] or native memory.
This FloatBuffer object does not eat any more memory (but uses the same memory the lightBuffer uses), but it could hinder the garbage collection of the ByteBuffer - but it seems you are reusing this one, don't you?
So there seems to be no problem so far (apart from you not knowing that there are several Buffers using the same memory).
Have you stored a reference to this buffer somewhere? I don't think that you can explicitly "remove the data" from the buffer and System.gc() won't help you here.
The garbage collector should handle this automatically for you unless you are maintaining a reference to this buffer.
Er, you don't.
The NIO buffers don't waste time with such things, they overwrite data in the buffer and move the marks.
If you want to get rid of it, you have to get rid of all references to it and make it available to the GC so it can be collected.