Байт-код Java LocalVariableTable содержит повторяющуюся запись для одной локальной переменной

Вот мой пример кода Java.

package com.test;

import javax.servlet.http.HttpServletRequest;

public class TestASMIns
{
    public void process(HttpServletRequest request)
    {
        String userName;
        if(request.getParameterMap().containsKey("username"))
        {
            userName = request.getParameter("username");
        }
        else
        {
            userName = "UNKNOWN";
        }
        System.out.println(userName);
    }
}

В приведенном выше примере локальная переменная userName объявлена ​​внутри тела метода. Инструкция байт-кода AFAIK для приведенного выше кода должна содержать только один LocalVariableNode для переменной 'userName' (поскольку он не объявлен ни в какой внутренней области/блоке). Но инструкция байт-кода для LocalVariableTable содержит повторяющуюся запись для переменной 'userName'. Может кто-нибудь пролить свет на это.

Вот инструкции байт-кода: (сгенерированы с использованием javap)

  public void process(javax.servlet.http.HttpServletRequest);
    descriptor: (Ljavax/servlet/http/HttpServletRequest;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=2
         0: aload_1
         1: invokeinterface #16,  1           // InterfaceMethod javax/servlet/http/HttpServletRequest.getParameterMap:()Ljava/util/Map;
         6: ldc           #22                 // String username
         8: invokeinterface #24,  2           // InterfaceMethod java/util/Map.containsKey:(Ljava/lang/Object;)Z
        13: ifeq          28
        16: aload_1
        17: ldc           #22                 // String username
        19: invokeinterface #30,  2           // InterfaceMethod javax/servlet/http/HttpServletRequest.getParameter:(Ljava/lang/String;)Ljava/lang/String;
        24: astore_2
        25: goto          31
        28: ldc           #34                 // String UNKNOWN
        30: astore_2
        31: getstatic     #36                 // Field java/lang/System.out:Ljava/io/PrintStream;
        34: aload_2
        35: invokevirtual #42                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        38: return
      LineNumberTable:
        line 10: 0
        line 12: 16
        line 13: 25
        line 16: 28
        line 18: 31
        line 19: 38
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      39     0  this   Lcom/test/TestASMIns;
            0      39     1 request   Ljavax/servlet/http/HttpServletRequest;
           25       3     2 userName   Ljava/lang/String;
           31       8     2 userName   Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 28 /* same */
        frame_type = 252 /* append */
          offset_delta = 2
          locals = [ class java/lang/String ]
}

person Parthi P    schedule 10.08.2017    source источник
comment
Локальная переменная считается определенной только в тех диапазонах байткодов, где ей присвоено значение, т.е. не является неинициализированным мусором. Поскольку вы не инициализировали его в объявлении, он не назначается с 0 по 24, назначается с номером 25 (одна инструкция длиной 3), не назначается с 28 по 30 и назначается с 31 по 38.   -  person dave_thompson_085    schedule 10.08.2017
comment
@ dave_thompson_085 Спасибо за разъяснение. Попытка инициализации String userName = null; дает ожидаемый результат. Но у меня есть одно сомнение. В приведенном выше примере значение, инициализированное с помощью блоков условий, должно быть доступно до конца тела метода, верно? то есть обе userName LocalVariable должны содержать диапазон до 38, верно? Почему if(true) блок LocalVariable заканчивается индексом 30?   -  person Parthi P    schedule 11.08.2017
comment
Причина была объяснена здесь stackoverflow.com/questions/26632799/   -  person Parthi P    schedule 11.08.2017
comment
По индексу 13 существует условный переход ifeq к местоположению 28, который будет выполнен, если containsKey вернет false. Если это будет сделано, для userName еще не будет допустимого значения. Инструкции 28: ldc #34; 30: astore_2 хранят в себе константную строку "UNKNOWN", поэтому после завершения инструкции astore_2 будет допустимое значение. Следовательно, для диапазона [28 - 30] переменная не имеет допустимого значения. Но это не имеет отношения к используемой памяти, так как слот номер 2 будет зарезервирован на протяжении всего выполнения метода независимо от того, содержит он допустимое значение или нет.   -  person Holger    schedule 05.09.2017