JavaFX TableView WeakListChangeListener Memory Leak? - java

I have a JavaFX TableView which is populated using property objects from a bean. All the columns have properties but in the CellFactory update() method some graphical components are used for displaying data such as an HBox with several children.
There is also a row factory which sets a style on the whole row when a status of that row changes.
The data in the table is updated and resorted in an array list which is a copy of the items in the table created from the observable list. Once the copy list is manipulated in various ways the table items are updated by doing an observableList.setAll(copy), I've also tried a observableList.clear(), addAll(copy) to see if it makes any difference.
The problem is that over time there appears to be a memory leak. Using MAT (https://eclipse.org/mat/) the leak suspects show the TableView visibleLeafColumns data structure seems to hold an enormous quantity WeakListChangeListener objects which are not being garbage collected. There are 6 columns in the table which has 250 rows (being manipulated every minute or so) but after running for about 30mins there are 80,000 WeakListChangeListeners registered!
Looking at the source code for the TableCell and assuming I'm reading it correctly it appears that the visibleLeafColumns is only added to when a new TableCell is created but I thought that the table cells were reused by the TableView and therefore shouldn't be created. (http://hg.openjdk.java.net/openjfx/2.2/master/rt/file/e71070b0e0c0/javafx-ui-controls/src/javafx/scene/control/TableCell.java)
I found a similar post from a couple of years ago here (https://community.oracle.com/thread/2354497?tstart=0) and I wondered if anyone else has come across a similar problem.
Platform: Redhat Linux v6
Java: 1.7.0 update 67
Any pointers would be really really appreciated.
Thanks,
Andy

There is a Jira bug report for this issue RT-34970. My colleague solved this issue by periodically removing all the columns and adding them back. That caused the TableView to tidy up the visibleLeafColumn object and the heap stayed sensible.
Thanks Jewelsea and Tomas for your suggestions. We did try Java 8 which didn't have this memory problem but the performance seemed to degrade over time (12hrs ish). This appeared to be related to animations so we'll investigate that properly when we make the jump to Java 8 in the future (cache hint didn't help).

Related

In populating an ObservableList, do I have to load ALL the records from my database?

So I'm porting my Swing Java database application to Java FX (still a beginner here, I recently just learned the basics of FXML and the MVC pattern, so please bear with me).
I intend to load the data from my existing database to the "students" ObservableList so I can show it on a TableView, but on my original Swing code, I have a search TextField, and when the user clicks on a button or presses Enter, the program:
Executes an SQLite command that searches for specific records, and retrieves the RecordSet.
Creates a DefaultTableModel based on the RecordSet contents
And throws that TableModel to the JTable.
However, Java FX is a completely different beast (or at least it seems so to me--don't get me wrong, I love Java FX :D ) so I'm not sure what to do.
So, my question is, do I have to load ALL the students in the database, then use some Java code to filter out students that don't fit the search criteria (and display all students when the search text is blank), or do I still use SQLite in filtering and retrieving records (which means I need to clear the list then add students every time a search is performed, and maybe it will also mess up with the bindings? Maybe there will be a speed penalty on this method also? Besides that, it will also reset the currently selected record because I clear the list--basically, bad UI design and will negatively impact the usability)
Depending on the right approach, there is also a follow-up question (sorry, I really can't find the answer to these even after Googling):
If I get ALL students from database and implement a search feature in Java, won't it use up more RAM than it should, because I am storing ALL the database data in RAM, instead of just the ones searched for? I mean, sure, even my lowly laptop has 4GB RAM, but the feeling of using more memory than I should makes me feel somewhat guilty LOL
If I choose to just update the contents of the ObservableList every time a new search has been performed, will it mess up with the bindings? Do I have to set up bindings again? How do I clear the contents of the ObservableList before adding the new contents?
I also have the idea of just setting the selected table item to the first record that matches the search string but I think it will be difficult to use, since only one record can be highlighted per search. Even if we highlight multiple rows, it'd be difficult to browse all selected items.
Please give me the proper way, not the "easy" way. This is my first time implementing a pattern (MVC or am I actually doing MVP, I don't know) and I realized how unmaintainable and ugly my previous programs are because I used my own style. This is a relatively big project that I need to support and improve for several years so having clean code and doing stuff the right way should help in maintaining the functionality of this program.
Thank you very much in advance for your help, and I hope I don't come off as a "dumb person who can't even Google" in asking these questions. Please bear with me here.
Basic design tradeoffs
You can, of course, do this either of the ways you describe. The basic tradeoffs are:
If you load everything from the database, and filter the table in Java, you use more memory (though not as much as you might think, as explained below)
If you filter from the database and reload every time the user changes the filter, there will be a bigger latency (delay) in displaying the data, as a new query will be executed on the database, with (usually) network communication between the database and the application being the biggest bottleneck (though there are others).
Database access and concurrency
In general, you should perform database queries on a background thread (see Using threads to make database requests); if you are frequently making database queries (i.e. filtering via the database), this gets complex and involves frequently disabling controls in the UI while a background task is running.
TableView design and memory management
The JavaFX TableView is a virtualized control. This means that the visual components (cells) are created only for visible elements (plus, perhaps, a small amount of caching). These cells are then reused as the user scrolls around, displaying different "items" as required. The visual components are typically quite memory-consumptive (they have hundreds of properties - colors, font properties, dimensions, layout properties, etc etc - most of which have CSS representations), so limiting the number created saves a lot of memory, and the memory consumption of the visible part of the table view is essentially constant, no matter how many items are in the table's backing list.
General memory consumption computations
The items observable list that forms the table's backing list contains only the data: it is not hard to ballpark-estimate the amount of memory consumed by a list of a given size. Strings use 2 bytes per character, plus a small fixed overhead, doubles use 8 bytes, ints use 4 bytes, etc. If you wrap the fields in JavaFX properties (which is recommended), there will be a few bytes overhead for each; each object has an overhead of ~16 bytes, and references themselves typically use up to 8 bytes. So a typical Student object that stores a few string fields will usually consume of the order of a few hundred bytes in memory. (Of course, if each has an image associated with it, for example, it could be a lot more.) Thus if you load, say 100,000 students from a database, you would use up of the order of 10-100MB of RAM, which is pretty manageable on most personal computer systems.
Rough general guidelines
So normally, for the kind of application you describe, I would recommend loading what's in your database and filtering it in memory. In my usual field of work (genomics), where we sometimes need 10s or 100s of millions of entities, this can't be done. (If your database contains, say, all registered students in public schools in the USA, you may run into similar issues.)
As a general rule of thumb, though, for a "normal" object (i.e. one that doesn't have large data objects such as images associated with it), your table size will be prohibitively large for the user to comfortably manage (even with filtering) before you seriously stretch the memory capacity of the user's machine.
Filtering a table in Java (all objects in memory)
Filtering in code is pretty straightforward. In brief, you load everything into an ObservableList, and wrap the ObservableList in a FilteredList. A FilteredList wraps a source list and a Predicate, which returns true is an item should pass the filter (be included) or false if it is excluded.
So the code snippets you would use might look like:
ObservableList<Student> allStudents = loadStudentsFromDatabase();
FilteredList<Student> filteredStudents = new FilteredList<>(allStudents);
studentTable.setItems(filteredStudents);
And then you can modify the predicate based on a text field with code like:
filterTextField.textProperty().addListener((obs, oldText, newText) -> {
if (newText.isEmpty()) {
// no filtering:
filteredStudents.setPredicate(student -> true);
} else {
filteredStudents.setPredicate(student ->
// whatever logic you need:
student.getFirstName().contains(newText) || student.getLastName().contains(newText));
}
});
This tutorial has a more thorough treatment of filtering (and sorting) tables.
Comments on implementing "filtering via queries"
If you don't want to load everything from the database, then you skip the filtered list entirely. Querying the database will almost certainly not work fast enough to filter (using a new database query) as the user types, so you would need an "Update" button (or action listener on the text field) which recomputed the new filtered data. You would probably need to do this in a background thread too. You would not need to set new cellValueFactorys (or cellFactorys) on the table's columns, or reload the columns; you would just call studentTable.setItems(newListOfStudents); when the database query finished.

Selecting and copying all items in a virtual SWT table

A user of my program ran into this issue with SWT's virtual tables: If one presses Ctrl+A and Ctrl+C in the table, not all elements will be copied to the clipboard, only the ones that have already been loaded.
This leads to a nasty surprise if the user relies on the false assumption that all table entries have been copied. Is there any reasonable (and if possible, unobtrusive) workaround to deal with this problem?
The SWT Table does not itself support copying its content, that must be part of the application ocde. Therefore I assume that you collect the text of the items (i.e. item.getText()) and then copy them to the clipboard.
To copy the entire contents of the table you would have to force all items to be materialized, for example by stepwise calling setTopIndex(), which will likely cause flicker.
I recommend to solve this on the model level. I.e. rewrite the copy code so that it uses the tables underlying data model to collect the necessary information.

JavaFx TableView CRUD for large database tables

I'm looking for sample apps or documentation for displaying a large table in JavaFx TableView , without loading the entire table into memory beforehand. CRUD capabilities would be nice, too, though I can write this myself if necessary.
All the examples I have found pre-load data into a an observable list (in memory, I assume), which I can't do for, say, 5 million records!
I have worked in Java Swing with table models that get their data from JDBC ResultSets, is there something similar I can do for JavaFx TableView? I also added my own sort and column layout persistence in Swing, and am looking to do something similar in JavaFx.
This may be a "beginner" question, or it's possible that I missed something simple while scouring through the JavaFx info.
Each TableView must has a model class. you can easily set your model class to table view.
For example you have class Student with (id,name,avg)
define an ObservableList of Student just like
ObservableList<Student> stdList = FXCollections.observableArrayList();
then set the list to the table view by
tableView.setDate(stdList);
and Now you can do anything you imagine (CRUD) on the list.

Does SWT's virtual tables release TableItems

In the context of displaying databases rows in an SWT VIRTUAL Table I am wondering if created TableItems are ever released by SWT in order for them to be garbage collected ?
Using virtual table allows us not to load the full model into memory by asking on the fly data to the database each time SWT needs it (through the SWT.setData listener). I am now wondering if an out of memory error can occur if the user scrolls for a very long time in a big table and thus all TableItem that have been displayed are somewhere in memory ?
Thanks in advance
Manu
After several investigations I confirm that SWT never releases the created TableItem. But, this is not a problem because of the Virtual style and SWT's power, a small amount of such items is created even if the user performs quick scrolling over all the table.

Java Entity Beans are not deleting rows from database; only from JTable

I am using Entity bean in NetBeans, to develop some master/detail forms. When I run the forms, I click the Delete JButton, and the row dissapears from the JTable.
But when I click on "Reload", the supposedly deleted row shows up again. I don't know why is this happening; why does the Entity doesn't erase all the way to the database table, and just deletes it out of the JTable?
This sort of issue sounds like it's related to the separation of the data (the model) from the view. I don't have specific knowledge regarding your technologies used, but hopefully I can provide some insight into what is the root of your problem.
In your case, it sounds like when you "Delete" you're only removing it from the view; you're not actually manipulating the data in any way (i.e. the model is not aware of this deletion).
Therefore once you "Reload" - which usually means that the view asks the model for what data to present - your "deletion" is lost since the model hasn't changed at all, and thus provides the exact same data to the view.
This sort of behavior is likely to occur when you're manipulating the data (i.e. deleting things) via the JTree itself or even the contained TreeNode objects, rather than the underlying TreeModel.
Hopefully this information helps you, sorry I don't have a more specific answer.
The JTable when reloaded, brought the record deleted because it had a foreign key link and couldn't deleted it at database level.

Categories

Resources