Страницы стека HotSpot. Красные / Желтые зоны и причины их возникновения - PullRequest
2 голосов
/ 08 июля 2019

Организация стека Java Threads описывается следующей схемой в комментариях .Таким образом, страница защиты 1 glibc представляется значением по умолчанию, которое устанавливается pthread_attr_init(pthread_attr*).

Причиной, которую я мог представить за красными / желтыми зонами, является обработка переполнения стека без получения SIGSEGV.Также красная зона здесь определенно означает нечто отличное от того, что указано в System V AMD64 ABI , для которого установлено 128 байт ниже rsp и предназначено для того, чтобы его не трогали обработчики прерываний / сигналов.

Красная зона HotSpot управляется опцией -XX:StackRedPages, и, насколько я пытался, она может иметь размер от 0 до 3.

Я также обнаружил, что когда первичная нить создаетпоток, который выполняет шаги инициализации, он устанавливает размер защиты pthread равным 0 .

Q: Для чего предназначены красные / желтые зоны и почему поток выполняетинициализация создается без pthread защитных страниц?

1 Ответ

1 голос
/ 11 июля 2019

В Linux одно (возможно, из некоторых других) использование зон red и yellow_reserved - заставить JVM HotSpot обрабатывать переполнение стека. На pthreads_attr_setguardsize странице справки есть примечание об установке размера защиты на 0:

ответственность за переполнение стека лежит на приложении (возможно, используя mprotect(2), чтобы вручную определить охранную зону на конец выделенного им стека).

и

Установка размера защиты 0 может быть полезна для сохранения памяти в приложение, которое создает много потоков и знает, что переполнение стека никогда не может произойти.

Таким образом, поскольку никакие потоки (даже первичные) не используют отображение MAP_GROWSDOWN для своего стека, устанавливая размер защиты в 0 в этой точке , вероятно, можно рассматривать как оптимизацию. «Переполнение стека никогда не происходит» гарантируется страницами reserved / yellow / red, которые JVM вручную отображает в функции void JavaThread::create_stack_guard_pages().

Как видно, некоторая часть внизу стека потока даже без защитных страниц pthread обрабатывается как HotSpot Guard Pages , то есть охраняется с mprotect Правильно, как указала нам страница руководства.

Фактическая обработка переполнения стека выполняется с помощью обработчика сигнала , который устанавливается при запуске JVM. Часть, связанная с переполнением стека, выглядит так (немного длиннее):

// Handle ALL stack overflow variations here
if (sig == SIGSEGV) {
  address addr = (address) info->si_addr;
  // check if fault address is within thread stack
  if (thread->on_local_stack(addr)) {
    // stack overflow
    if (thread->in_stack_yellow_reserved_zone(addr)) {
      if (thread->thread_state() == _thread_in_Java) {
        if (thread->in_stack_reserved_zone(addr)) {
          frame fr;
          if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) {
            assert(fr.is_java_frame(), "Must be a Java frame");
            frame activation =
              SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
            if (activation.sp() != NULL) {
              thread->disable_stack_reserved_zone();
              if (activation.is_interpreted_frame()) {
                thread->set_reserved_stack_activation((address)(
                  activation.fp() + frame::interpreter_frame_initial_sp_offset));
              } else {
                thread->set_reserved_stack_activation((address)activation.unextended_sp());
              }
              return 1;
            }
          }
        }
        // Throw a stack overflow exception.  Guard pages will be reenabled
        // while unwinding the stack.
        thread->disable_stack_yellow_reserved_zone();
        stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
      } else {
        // Thread was in the vm or native code.  Return and try to finish.
        thread->disable_stack_yellow_reserved_zone();
        return 1;
      }
    } else if (thread->in_stack_red_zone(addr)) {
      // Fatal red zone violation.  Disable the guard pages and fall through
      // to handle_unexpected_exception way down below.
      thread->disable_stack_red_zone();
      tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
      // This is a likely cause, but hard to verify. Let's just print
      // it as a hint.
      tty->print_raw_cr("Please check if any of your loaded .so files has "
                        "enabled executable stack (see man page execstack(8))");
    } else {
       //...

Как видно из кода. Если SIGSEGV поднимается, когда в зарезервированной или желтой зоне защитные страницы не охраняются, ошибка StackOverflowError отправляется в Java, и защита снова включается при разматывании стека.

В отличие от этого, если SIGSEGV поднимается, когда в красной зоне он рассматривается как "неисправимая ошибка переполнения стека".

Например:

public class Main {
    static void foo() throws Exception {
        foo();
    }

    public static void main(String args[]) throws Exception {
        foo();
    }
}

как можно заметить с gdb, это StackOverflowError произошло в зарезервированной / желтой зоне, а не в красной (поэтому, вероятно, это причина того, что это может быть обработано обработчиком try - catch).

В заключение следует отметить, что красная зона охранника HotSpot имеет совершенно другое значение, чем AMD64 System V ABI определение красной зоны (просто область, в которую вызывающий абонент может поместить локальные переменные, чтобы они не были затронуты сигналом / прерыванием обработчик или, возможно, отладчик, поскольку gdb может поместить свои собственные данные в стек за пределами красной зоны).

...