[Это работает правильно однопоточным]Возможной ошибка нативного освобождения памяти в Java
EDIT: Тестирование проходит на Windows 8; Не удается последовательно на Ubuntu 14.04
EDIT 2: Текущее мышление, что это *, связанные с Никс вопрос о получении надлежащей информации об использовании памяти.
Я ищу подтверждение от гуру здесь о переполнении стека, что это действительно проблема и что я не представляю ее.
Я работал с памятью alloc/dealloc в Java, используя Unsafe. Я нашел какое-то действительно странное поведение и не мог понять, что делаю, чтобы не выпускать память. Я сделал тест Vanilla, который не использует скрытые API, чтобы показать проблему. Кажется, что Unsafe.releaseMemory и базовый перевод между указателями памяти VM и OS терпят неудачу при многопоточности.
Когда программа запускается, вам нужно посмотреть PID в первой строке и открыть TOP в терминале, используя «top -p pid». Первоначальная память RES должна составлять около 30M. Если виртуальная машина вызывает проблему, она будет иметь гораздо больше памяти.
Результат будет выглядеть так:
[email protected] Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 25.31-b07
Linux amd64 3.16.0-30-generic
Press any key to start
Tester 4 remaining 49
Tester 3 remaining 49
Tester 2 remaining 49
Tester 1 remaining 49
Tester 3 remaining 48
Tester 4 remaining 48
Tester 2 remaining 48
Tester 1 remaining 48
TOP должны сообщать информацию, как это. Вы можете увидеть утечку памяти. Проверка BufferPool MX Bean покажет, что Java THINKS выделяет 0 байт.
[email protected]:~$ top -d 1 -p 31067 | grep java
31067 jon 20 0 6847648 27988 15420 S 0.0 0.2 0:00.09 java
31067 jon 20 0 7769264 743952 15548 S 315.5 4.6 0:03.25 java
31067 jon 20 0 7900336 847868 15548 S 380.1 5.3 0:07.06 java
31067 jon 20 0 7834800 810324 15548 S 379.1 5.0 0:10.86 java
31067 jon 20 0 7703728 700028 15548 S 379.2 4.3 0:14.66 java
31067 jon 20 0 7900336 894940 15548 S 379.2 5.5 0:18.46 java
31067 jon 20 0 7703728 674400 15548 S 277.5 4.2 0:21.24 java
31067 jon 20 0 7376048 430868 15548 S 59.9 2.7 0:21.84 java
31067 jon 20 0 7376048 430868 15548 S 0.0 2.7 0:21.84 java
31067 jon 20 0 7376048 430868 15548 S 1.0 2.7 0:21.85 java
31067 jon 20 0 7376048 430868 15548 S 0.0 2.7 0:21.85 java
31067 jon 20 0 7376048 430868 15548 S 1.0 2.7 0:21.86 java
Это класс. Вы можете либо вызвать cleaner() напрямую, либо использовать q.poll() и позволить System.gc() попытаться его очистить. Это не показывает проблему КАЖДОЕ время, но большую часть времени.
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Queue;
import sun.nio.ch.DirectBuffer;
public class VanillaMemoryTest
{
public static void main(String[] args)
{
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
System.out.println(runtime.getName() + " "+ runtime.getVmVendor() + " " + runtime.getVmName() + " "+ runtime.getVmVersion());
System.out.println(os.getName() + " " + os.getArch() + " " + os.getVersion());
System.out.println("Press any key to start");
try
{
System.in.read();
}
catch (IOException e1)
{
}
Thread one = new Thread(new Tester());
one.setName("Tester 1");
Thread two = new Thread(new Tester());
two.setName("Tester 2");
Thread three = new Thread(new Tester());
three.setName("Tester 3");
Thread four = new Thread(new Tester());
four.setName("Tester 4");
one.start();
two.start();
three.start();
four.start();
try
{
four.join();
}
catch (InterruptedException e)
{
}
System.out.println("Press any key to exit");
try
{
System.in.read();
}
catch (IOException e1)
{
}
}
private static class Tester implements Runnable
{
public void run()
{
try
{
Queue<ByteBuffer> q = new ArrayDeque<ByteBuffer>();
int total = 50;
while(total > 0)
{
try
{
for (int x = 0; x < 10; x++)
{
ByteBuffer b;
b = ByteBuffer.allocateDirect(1000 * 1000 * 30);
q.offer(b);
}
}
catch (Throwable e)
{
e.printStackTrace();
}
while (q.size() > 0)
{
//q.poll();
((DirectBuffer) q.poll()).cleaner().clean();
}
System.out.println(Thread.currentThread().getName() + " remaining " + (--total));
}
}
catch (Throwable p)
{
p.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " exit");
System.gc();
}
}
}
** UPDATE, вот некоторые HOTSPOT ИСХОДНЫЙ КОД **
Поскольку я потратил кучу времени вчера, глядя на исходный код Hotspot ... Я нарисую каждому картину.
Важно понимать, что Java всегда устанавливает новые распределения памяти с нулями; поэтому он должен отображаться в ВИЭ.
Unsafe.freeMemory родной C-код. addr_from_java ничего не делает.
UNSAFE_ENTRY(void, Unsafe_FreeMemory(JNIEnv *env, jobject unsafe, jlong addr))
UnsafeWrapper("Unsafe_FreeMemory");
void* p = addr_from_java(addr);
if (p == NULL) {
return;
}
os::free(p);
UNSAFE_END
вызовы ОС: бесплатно (уведомление #ifdef Assert) в противном случае было бы сырым вызов родной C :: бесплатно() метод. Я собираюсь предположить, что ASSERT является ложным в публичном производстве VM. Это оставляет нам MemTraker :: record_free, что может быть проблемой, или Ubuntu, возможно, просто потерял способность освобождать память. lol
void os::free(void *memblock, MEMFLAGS memflags) {
NOT_PRODUCT(inc_stat_counter(&num_frees, 1));
#ifdef ASSERT
if (memblock == NULL) return;
if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) {
if (tty != NULL) tty->print_cr("os::free caught " PTR_FORMAT, memblock);
breakpoint();
}
verify_block(memblock);
NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap());
// Added by detlefs.
if (MallocCushion) {
u_char* ptr = (u_char*)memblock - space_before;
for (u_char* p = ptr; p < ptr + MallocCushion; p++) {
guarantee(*p == badResourceValue,
"Thing freed should be malloc result.");
*p = (u_char)freeBlockPad;
}
size_t size = get_size(memblock);
inc_stat_counter(&free_bytes, size);
u_char* end = ptr + space_before + size;
for (u_char* q = end; q < end + MallocCushion; q++) {
guarantee(*q == badResourceValue,
"Thing freed should be malloc result.");
*q = (u_char)freeBlockPad;
}
if (PrintMalloc && tty != NULL)
fprintf(stderr, "os::free " SIZE_FORMAT " bytes --> " PTR_FORMAT "\n", size, (uintptr_t)memblock);
} else if (PrintMalloc && tty != NULL) {
// tty->print_cr("os::free %p", memblock);
fprintf(stderr, "os::free " PTR_FORMAT "\n", (uintptr_t)memblock);
}
#endif
MemTracker::record_free((address)memblock, memflags);
::free((char*)memblock - space_before);
}
Это сообщение об ошибке или есть вопрос где-то? –
В поисках подтверждения толпы. – bond
Попробуйте взглянуть на VM availableMemory вместо "top". * Топ включает память, «обещанную» приложению ОС, даже если она никогда не используется *. Память может быть бесплатной и доступной. –