Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8222325

EventDispatchChainImpl.java reset() method doesn't reset dispatchers[] leading to memory leak

    Details

    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      linux_ubuntu

      Description

      ADDITIONAL SYSTEM INFORMATION :
      OS: ubuntu16.04 64bit
      Java jdk1.8.0_201, jdk1.8.0_72, jdk1.8.0_66 and so on.

      A DESCRIPTION OF THE PROBLEM :
      "com/sun/javafx/event/EventDispatchChainImpl.java" reset() method doesn't "null clear" dispatchers[] array and that some elements holding reference in it is not GCed.
       As the attached test program, Events are prepended to its dispatchers[] and are dispatched, but at some point when "chain.reset()" method is called to reset the array expecting all are "zero" or "null"cleared , but actually some elements of dispatches[] array are not null cleared.
      We expect "dispatchers[]" array is all null filled out and no references are in dispatches[] array for the future GC, but actually there remains some references . This will lead to a memory leak.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1 To compile with jdk8, for example
          $ /usr/java/jdk1.8.0_201/bin/javac EventDispatchChainImplTest.java
      2 Execuing the compiled class
          $ /usr/java/jdk1.8.0_201/jre/bin/java EventDispatchChainImplTest

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      ==<test0>==
      chain-1
      chain-2
      [null, null, null, null, null, null, null, null]
      ==<test1>==
      [null, null, null, null, null, null, null, null]

      ACTUAL -
      ==<test0>==
      chain-1
      chain-2
      [null, EventDispatchChainImplTest$$Lambda$3/1828972342@5fd0d5ae, null, null, null, null, null, null]
      ==<test1>==
      [null, null, EventDispatchChainImplTest$1$$Lambda$38/1297685781@48cf768c, null, null, null, null, null]

      ---------- BEGIN SOURCE ----------
      import java.lang.reflect.Field;
      import java.util.Arrays;

      import com.sun.javafx.event.EventDispatchChainImpl;
      import com.sun.javafx.event.EventUtil;

      import javafx.event.Event;
      import javafx.event.EventDispatchChain;
      import javafx.event.EventDispatcher;
      import javafx.scene.Group;
      import javafx.scene.input.MouseEvent;

      public class EventDispatchChainImplTest {

          //
          // "test0" is implemented with com.sun.javafx.event.*
          //
          public void test0() {
              EventDispatchChainImpl chain = new EventDispatchChainImpl();
              chain.prepend((e, h) -> {
      System.out.println("chain-1");
      h.prepend((e1, h1) -> {
      System.out.println("chain-2");
      return h1.dispatchEvent(e);
      });
      return h.dispatchEvent(e);
      });

      //Dispatces Event
              chain.dispatchEvent(new Event(MouseEvent.ANY));

      //Reset chain's properties
      //And we expect "chain.dispatchers[] " array is filled with "null"
              chain.reset();

      //Check if "chain.dispatchers[]" is filled with "null" or not.
      // But actually it is not null cleared. and it holding a reference. This is a bug.
              logEventDispatchChainImpl(chain);
          }

          //
          // "test1" is implemented with javafx.event.*
          //
          public void test1() {
              Group g = new Group();
              EventDispatcher dispatcher = new EventDispatcher() {
      @Override
      public Event dispatchEvent(Event event, EventDispatchChain tail) {
      tail.prepend((e, d) -> {
      return d.dispatchEvent(e);
      });
      return tail.dispatchEvent(event);
      }
      };
              g.setEventDispatcher(dispatcher);

      // Fire any event
              Event.fireEvent(g, new Event(MouseEvent.ANY));

      // Check if " EventUtil.eventDispatchChain.dispatchers" is filled with "null" or not.
      // Actually it is not null cleared. and it holding a reference. This is a bug,too.
              logEventUtil();

          }

          static void logEventUtil() {
              try {
                  Field eventDispatchChainField = EventUtil.class.getDeclaredField("eventDispatchChain");
                  eventDispatchChainField.setAccessible(true);
                  EventDispatchChainImpl eventDispatchChainImpl = (EventDispatchChainImpl) eventDispatchChainField
      .get(EventUtil.class);
                  logEventDispatchChainImpl(eventDispatchChainImpl);
              } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                  e.printStackTrace();
              }
          }

          static void logEventDispatchChainImpl(EventDispatchChainImpl chain) {
              try {
                  Field dispatchersField = EventDispatchChainImpl.class.getDeclaredField("dispatchers");
                  dispatchersField.setAccessible(true);
                  EventDispatcher[] dispatchers = (EventDispatcher[]) dispatchersField.get(chain);
                  System.out.println(Arrays.toString(dispatchers));
              } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {

                  e.printStackTrace();
              }
          }

          public static void main(String[] args){
      EventDispatchChainImplTest test = new EventDispatchChainImplTest();
      System.out.println("==<test0>==");
      test.test0();
      System.out.println("==<test1>==");
      test.test1();
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      As a workaround , I modified reset() method in com.sun.javafx.event.EventDispatchChainImpl.java

      Original:
            public void reset() {
                // shrink?
      ! for (int i = 0; i < reservedCount; ++i) {
                    dispatchers[i] = null;
                }
        
      My workaround:

            public void reset() {
                // shrink?
      ! for (int i = 0; i < dispatchers.length; ++i) {
                    dispatchers[i] = null;
                }
        . As of now, it looks it's working.

      FREQUENCY : always


        Attachments

          Activity

            People

            • Assignee:
              arapte Ambarish Rapte
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

              • Created:
                Updated: