Opened 4 years ago
Closed 11 months ago
#19885 closed defect (fixed)
memory leak with "temporary" objects in validator and actions
Reported by: | GerdP | Owned by: | GerdP |
---|---|---|---|
Priority: | normal | Milestone: | Longterm |
Component: | Core | Version: | |
Keywords: | Cc: |
Description
I found some places where ways or relations are cloned for temporary use. This is likely to cause a memory leak since the clone might refer to child objects and those child objects will also refer to the clone unless the clone is correctlly cleaned up. Pattern:
// create full clone Way wnew = new Way(w); // do something with the clone .. // make sure that the clone is cleaned up if no longer needed wnew.setNodes(null);
Attachments (3)
Change History (51)
comment:1 by , 4 years ago
comment:2 by , 4 years ago
Milestone: | → 20.10 |
---|---|
Owner: | changed from | to
Status: | new → assigned |
comment:3 by , 4 years ago
Ouch! The relation editor also creates clones and doesn't care about cleanup. It would be quite difficult to fix this problem in all the code in core and all plugins. Sometimes commands are created but not executed because user cancels the operation.
Maybe it is possible to write a "garbage collector" routine which would remove those parent objects which don't belong to the current dataset and which are not used in any relation editor window or for any command in the UndoRedoHandler
.
The routine would be rather simple, we just have to make sure that is not executed while one of the cloned parents is still in use.
by , 4 years ago
Attachment: | 19885-wip.patch added |
---|
comment:4 by , 4 years ago
Patch fixes various memory leaks and implements a new consistency check which helps to find more.
Worst case so far: When you move the mouse so that the virtual middle node of a way segment is highlighted this will create (multiple) new parent ways.
comment:5 by , 4 years ago
I think there is really no way to avoid those leaks while a complex action like SplitWayAction
is computing the effects of a split on all the ways and parent relations. We have to ignore those minor leaks where a fix would require more code.
comment:13 by , 4 years ago
org.openstreetmap.josm.actions.JoinAreasActionTest.testExamples fails → https://josm.openstreetmap.de/jenkins/job/JOSM/jdk=JDK8/lastCompletedBuild/testReport/org.openstreetmap.josm.actions/JoinAreasActionTest/
comment:15 by , 4 years ago
Replying to GerdP:
In 17167/josm:
Hmm, wouldn't it be better to fix ChangePropertyCommand so it can be used for that purpose?
comment:16 by , 4 years ago
WIll have a look tomorrow. I first thought that it works similar to ChangeNodesCommand
which also doesn't allow an empty list, but a way without nodes really makes no sense while a way without tags can be valid.
I also think that a ChangeRelationMembersCommand
would help.
comment:17 by , 4 years ago
Hmm, wouldn't it be better to fix ChangePropertyCommand so it can be used for that purpose?
The command is used by varios plugins and the current code does nothing when the map is empty. Up to now the command works more like a "AddOrReplacePropertyCommand". I'd rather add a "RemoveAllPropertiesCommand".
comment:18 by , 4 years ago
See #19924 for a regression. Have to check the other changes for this ticket now...
comment:19 by , 4 years ago
Good news: ExtrudeAction
was the only one where I replaced Way.remove()
by List.remove()
comment:21 by , 4 years ago
Simply dropping 6 lines to make the code better is surely forbidden somewhere in programming guidelines. 😂
comment:22 by , 4 years ago
Milestone: | 20.10 → Longterm |
---|
This will take longer. Detected issues so far:
- Actions create a
ChangeCommand
instead of aChangeNodesCommand
orRemoveNodesCommand
- Actions create a
ChangeNodesCommand
instead of aRemoveNodesCommand
, e.g. if a single node is removed from a way with 1000 nodes. - For relations we need commands to change the member list or remove members, similar to those with way nodes
- New ways or relations should not be added with AddCommand, instead some kind of CreateParentCommand could do the same without the need to create the complete Way or Relation before.
- Similar problems with Commands that change properties (tags)
I think about one or more factory methods like these to reduce memory needed for the undo/redo stacks.
Command c = ChangeNodesCommand.createBestCommand(way, newNodes); Command c = ChangeMembersCommand.createBestCommand(rel, newMember}}} Command c = ChangePropertyCommand.createBestCommand(rel, tag}}}
Another issue is in the UndoRedoHandler:
If undo is used and not followed by redo the command stack simply removes some Commands from the stack. These commands are probably never GCed as they might still be refered to by child objects in the DataSet. Same is true for commands that are removed because limit for the stack size is reached.
comment:35 by , 4 years ago
Still some places to fix in core, not talking about plugins.
Some actions really need special care. I think we have error prone data flow like this:
newRel = new Relation(old); // change newRel ... cmds.add(new ChangeCommand(old, newRel)); ... // change newRel again newRel.put(key_xyz, "new value"); ... UndoRedoHandler.add(cmds); // executes the ChangeCommand with the latest status of newRel
When I replace the ChangeCommand by a ChangeMembersCommand I get of course different results.
comment:36 by , 4 years ago
I am making progress with the complex commands (split, join, combine). On my local system I see only a few dead links with core as long as I don't start to undo/redo things. Undoing an AddCommand
for a way with 10 nodes removes the way from the dataset but the links from the nodes to way stay intact. It would be cleaner to have a SequenceCommand
with an AddCommand
with an empty way and a ChangeNodesCommand
that adds the nodes, but this combination requires more memory.
Open brain teasers are the creation of relations via presets and "Update multipolygon".
comment:37 by , 4 years ago
The leak produced by the creation of relations might be fixed like this:
-
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java
31 31 import javax.swing.JOptionPane; 32 32 import javax.swing.JPanel; 33 33 import javax.swing.JToggleButton; 34 import javax.swing.SwingUtilities;35 34 36 35 import org.openstreetmap.josm.actions.AdaptableAction; 37 36 import org.openstreetmap.josm.actions.CreateMultipolygonAction; … … 433 432 if (r.isMultipolygon() && r != calculated) { 434 433 r.setMembers(RelationSorter.sortMembersByConnectivity(r.getMembers())); 435 434 } 436 SwingUtilities.invokeLater(() -> RelationEditor.getEditor(435 GuiHelper.executeByMainWorkerInEDT(() -> RelationEditor.getEditor( 437 436 MainApplication.getLayerManager().getEditLayer(), r, members).setVisible(true)); 437 MainApplication.worker.execute(() -> { 438 // Ugly hack to prevent memory leak, see #19885 439 // by now, relation editor has created a copy of r. 440 r.setMembers(null); 441 }); 438 442 } 439 443 if (!primitives.isEmpty()) { 440 444 DataSet ds = primitives.iterator().next().getDataSet();
comment:38 by , 4 years ago
Forget the ugly patch in comment:37. 19885-rel-editor.patch is a more general approach. Less ugly, but still quite confusing.
by , 4 years ago
Attachment: | 19885-splitway.patch added |
---|
control flow is really a mess, both in SplitWayAction
and in SplitWayCommand
comment:48 by , 11 months ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Closing this rather generic ticket. Remaining cases should be handled in individual tickets.
A lot of these clones could be avoided. They are often created to produce a ChangeCommand:
Instead one may create a ChangeNodesCommand. This requires less code, is faster and probably requires less memory: