Причина, по которой ошибка сохраняется в JDK так долго, в основном из-за состояния гонки между отключением памяти и отображением фиктивной памяти, в результате чего может отображаться какая-то другая память (возможно, с помощью собственного кода). Я ознакомился с API-интерфейсами ОС, и на уровне системного вызова не существует атомарных операций с памятью, которые бы отображали файл и отображали что-то еще по тому же адресу. Однако есть решения, которые блокируют весь процесс, вытесняя отображение из-под него.
Unmap работает корректно в финализации без охраны, потому что сборщик мусора доказал, что объект недоступен первым, поэтому расы нет.
Специальное решение для Linux:
1) vfork ()
2) отправить родителю сигнал STOP
3) разархивировать память
4) отобразить нули на свое место
5) отправить родителю сигнал CONT
6) _exit (который разблокирует родительский поток)
В Linux изменения в отображении памяти распространяются на родительский.
Код на самом деле выглядит примерно так (vfork()
- помешанный человек):
int unmap(void *addr, int length)
{
int wstatus;
pid_t child;
pid_t parent;
int thread_cancel_state;
signal_set signal_set;
signal_set old_signal_set;
parent = getpid();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &thread_cancel_state);
sigfillset(&signal_set);
pthread_sigmask(SIG_SETMASK, &signal_set, &old_signal_set);
if (0 == (child = vfork()) {
int err = 0;
kill(parent, SIGSTOP);
if (-1 == munmap(addr, length))
err = 1;
else if ((void*)-1 == mmap(addr, length, PROT_NONE, MAP_ANONYMOUS, -1, 0);
err = 1;
kill(parent, SIGCONT);
_exit(err);
}
if (child > 0)
waitpid(child, &wstatus, 0);
else
wstatus = 255;
pthread_sigmask(SIG_SETMASK, &old_signal_set, &signal_set);
pthread_setcancelstate(thread_cancel_state, &thread_cancel_state);
return (wstatus & 255) != 0;
}
В Windows вы можете остановить все потоки, кроме этого, используя SuspendThread
, который выглядит специально для этого. Однако перечислять темы будет сложно, потому что вы боретесь против CreateThread
. Вы должны запустить API перечисления потока ntdll.dll
(вы не можете использовать ToolHelp здесь, поверьте мне) и SuspendThread
каждый, кроме своего собственного, осторожно используя только VirtualAlloc для выделения памяти, потому что SuspendThread
просто сломал все процедуры выделения кучи, и вам придется делать все это в цикле, пока вы не найдете больше.
Здесь есть некоторая рецензия, которую я не совсем чувствую, что могу точно отогнать:
http://forums.codeguru.com/showthread.php?200588-How-to-enumerate-threads-in-currently-running-process
Я не нашел никаких решений для Mac OSX.