Project

General

Profile

Bug #12112

Memory leak in client JVM caused by deleted dynamic windows persisting

Added by Șerban Bursuc about 1 hour ago. Updated 1 minute ago.

Status:
New
Priority:
High
Target version:
-
Start date:
Due date:
% Done:

0%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
reviewer:
production:
No
env_name:
topics:

Related issues

Related to User Interface - Bug #10530: Move cursor change logic from JS client to the FWD client WIP

History

#2 Updated by Șerban Bursuc about 1 hour ago

  • Related to Bug #10530: Move cursor change logic from JS client to the FWD client added

#3 Updated by Șerban Bursuc about 1 hour ago

This should affect all the customer projects that use UI and also create dynamic windows. It is not a regression and has been present for a while at the very least. Found during #10530 regression testing using cursor related testcases. Report below.

Symptom

A window created at runtime and then deleted (CREATE WINDOW, DELETE WIDGET) stays in the memory of the FWD client process, together with its frames, the widgets in them and their mouse actions. Nothing frees them later: every deleted window adds to the client heap, so a session that keeps opening and closing windows grows for as long as it runs. The windows do not have to be hovered or clicked, and no mouse pointer is involved; creating and deleting them is enough.

Per deleted window with one frame and three buttons, these stay live in the client JVM:

Class Instances per deleted window
WindowGuiImpl 1
FrameGuiImpl 1
ButtonGuiImpl 3
MouseHoverAction 3
MouseEmptySelector 1

Found while working on #10530. Same pattern as #10322.

Testcase

Each choose of the Cycle button creates and deletes 25 windows, each with a frame and three buttons:

/* Each choose of Cycle creates and deletes 25 windows, each with a frame and three buttons. */
DEFINE VARIABLE hWin AS HANDLE  NO-UNDO.
DEFINE VARIABLE hFrm AS HANDLE  NO-UNDO.
DEFINE VARIABLE hBtn AS HANDLE  NO-UNDO.
DEFINE VARIABLE i    AS INTEGER NO-UNDO.
DEFINE VARIABLE j    AS INTEGER NO-UNDO.

DEFINE BUTTON bCycle LABEL "Cycle".
DEFINE FRAME f bCycle.

ON CHOOSE OF bCycle
DO:
   DO i = 1 TO 25:
      CREATE WINDOW hWin
         ASSIGN WIDTH   = 40
                HEIGHT  = 10
                VISIBLE = TRUE.
      CREATE FRAME hFrm
         ASSIGN PARENT  = hWin
                WIDTH   = 38
                HEIGHT  = 8
                VISIBLE = TRUE.
      DO j = 1 TO 3:
         CREATE BUTTON hBtn
            ASSIGN FRAME     = hFrm
                   ROW       = j * 2
                   COLUMN    = 3
                   LABEL     = "B" + STRING(j)
                   SENSITIVE = TRUE
                   VISIBLE   = TRUE.
      END.
      DELETE WIDGET hWin.
   END.
END.

ENABLE bCycle WITH FRAME f.
WAIT-FOR WINDOW-CLOSE OF CURRENT-WINDOW.

Steps, FWD trunk rev 16818, web GUI client:

  1. Run the testcase and log in.
  2. Take the histogram below.
  3. Choose Cycle (25 windows created and deleted), take the histogram again.
  4. Choose Cycle again (50 windows), take the histogram again.

Measurement

Run jmap as the OS user the client JVM runs under. jmap -histo:live runs a full garbage collection first, so it counts only objects that are still reachable:

PID=$(pgrep -n -f '^java .*ClientDriver')
jmap -histo:live $PID | grep -E '#instances|^---|[.](WindowGuiImpl|FrameGuiImpl|ButtonGuiImpl|MouseHoverAction|MouseEmptySelector)$'

After the login (the default window, its frame and the Cycle button):

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 629:             1            448  com.goldencode.p2j.ui.client.gui.FrameGuiImpl
 800:             1            264  com.goldencode.p2j.ui.client.gui.WindowGuiImpl
1011:             1            160  com.goldencode.p2j.ui.client.gui.ButtonGuiImpl
2011:             1             40  com.goldencode.p2j.ui.client.gui.driver.MouseEmptySelector
2013:             1             40  com.goldencode.p2j.ui.client.gui.driver.MouseHoverAction

After 25 windows created and deleted:

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 126:            76          12160  com.goldencode.p2j.ui.client.gui.ButtonGuiImpl
 131:            26          11648  com.goldencode.p2j.ui.client.gui.FrameGuiImpl
 174:            26           6864  com.goldencode.p2j.ui.client.gui.WindowGuiImpl
 265:            76           3040  com.goldencode.p2j.ui.client.gui.driver.MouseHoverAction
 452:            26           1040  com.goldencode.p2j.ui.client.gui.driver.MouseEmptySelector

After 50 windows created and deleted:

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
  93:           151          24160  com.goldencode.p2j.ui.client.gui.ButtonGuiImpl
  97:            51          22848  com.goldencode.p2j.ui.client.gui.FrameGuiImpl
 127:            51          13464  com.goldencode.p2j.ui.client.gui.WindowGuiImpl
 203:           151           6040  com.goldencode.p2j.ui.client.gui.driver.MouseHoverAction
 331:            51           2040  com.goldencode.p2j.ui.client.gui.driver.MouseEmptySelector

Every deleted window remains: each 25 windows add 25 windows, frames and selectors and 75 buttons and hover actions. The histograms only list a few classes; everything these widgets reference stays as well.

Cause

Deleting the window calls destroy() for its own parts (title bar, caption buttons, status line, message area) but never for its frames and their widgets, so MouseHandler.deregisterWidget never runs for them. Their actions stay in the MouseHandler registry (WorkArea.actions): the buttons' MouseHoverAction (from AbstractWidget._setEnabled) and the frame's MouseEmptySelector (from AbstractGuiDriver.registerEmptySelector). Each action holds its widget (MouseWidgetAction.widget), which keeps the whole window reachable.

#4 Updated by Șerban Bursuc about 1 hour ago

  • Assignee set to Șerban Bursuc

If this hasn't been fixed yet in a branch I can take a look at this.

#5 Updated by Constantin Asofiei 20 minutes ago

Serban, please confirm the test in 4GL - what does valid-handle say for the hFrm and hBtn widgets? Because to me it doesn't look like is FWD's responsibility to clean this up.

#6 Updated by Șerban Bursuc 1 minute ago

Constantin Asofiei wrote:

Serban, please confirm the test in 4GL - what does valid-handle say for the hFrm and hBtn widgets? Because to me it doesn't look like is FWD's responsibility to clean this up.

Didn't consider the possibility of the 4GL being bad and you're right, 4GL does not delete the frame and its content either after deleting the window widget handle. This is an AI test generation issue, it should probably be put in a skill somewhere to do a proper cleanup.

There's still a difference though - when adding DELETE WIDGET hFrm to the testcase the client side memory is fixed but the server side seems to still grow. jmap shows 1 WindowWidget, 1 FrameWidget and 2 ButtonWidget widgets after each cycle. In OE all widgets become invalid when queried with VALID-HANDLE. I will draft the report once I double check the issue.

Also available in: Atom PDF