JavaFX Node objects are not garbage collected - memory-leaks

JavaFX (1.2.x and 1.3.x) doesn't seem to allow garbage collection for at least Nodes and Scenes. A Node object is not freed after being removed from Scene (there's no other explicit reference to it).
Here goes example:
var buttonB:Button =
Button {
text: "i'm just hanging here"
}
var buttonC:Button =
Button {
text: "hit me to leak memory"
action: function() {
buttonB.managed = false;
delete buttonB from mainBox.content;
buttonB.skin = null;
buttonB = null;
java.lang.System.gc();
}
}
def mainBox:HBox =
HBox {
hpos: HPos.CENTER
nodeVPos: VPos.CENTER
layoutInfo: LayoutInfo {
width: 800 height: 600
}
content: [buttonC, buttonB]
}
buttonB is never freed. Setting skin to null helps somehow (in VisualVM most of the references to the button disappear) but doesn't fix the issue. I also tried nullifying all members using JavaFX reflection with no luck.
Is it possible to make buttonB eligible for GC and how to do it?
Does the problem persist in JavaFX 2.0?

I found (through visualVM inspection) that JavaFX 1.3 keeps SoftReferences to buffered images (that probably represent rendered versions of Nodes) for nodes that have been removed. For me this was a sort of memory leak, as soft references are cleared depending on the amount of free memory. This isn't a memory leak (OutOfMemoryException will never happen due to this) but for me this was reason to cause very inefficient garbage collecting.
You can use XX:SoftRefLRUPolicyMSPerMB=<N> to reduce the time SoftReferences are kept, this is at a possible (but unlikely) performance penalty though. It sets the number of milliseconds per free MB that an object is kept. Default is 1000 ms.

Related

SwiftUI ColorPicker "opened" and "closed" events?

I need to be able to undo / redo the colors that are picked with the new SwiftUI ColorPicker ( on the iPad : it's presented as a floating window )
The thing that makes it very difficult is that there is apparently no way to know that the user has indeed chosen a color ( and therefore closed the panel )
Instead, the behavior of ColorPicker is that it will keep updating the binded color as the user is manipulating the color controls. This is very helpful to show a live preview, but you don't want to register all these color variations for undo / redo purposes : you only want the color that was finally picked.
Therefore there is no logical distinction between the colors that the user tried, and the one that was selected
And I looked everywhere : there aren't any modifiers / notifications related to that.
I know SwiftUI hasn't been there for long, but this seems like a crucial functionality that's missing?
Has anyone found a workaround?
Undo is always tricky. One size does not fit all. In this case a good solution is to throttle the number of events that come into the UndoManager. You can do so by comparing the current time to the time you last commit.
Create a view model that represents your Source of Truth, and conform to ObservableObject to publish events. It will own the UndoManager. It will have a computed property that gets/sets to the internal data, and check the current time against the time of last commit.
class EditViewModel: ObservableObject {
var commitTime = Date.now
let undoManager = UndoManager()
private var _color: Color = .red {
didSet {
objectWillChange.send()
}
}
var color: Color {
get { _color }
set {
let oldValue = color
let now = Date.now
if now.timeIntervalSince(commitTime) > 1 {
undoManager.registerUndo(withTarget: self) {
$0.color = oldValue
}
}
self.commitTime = now
_color = newValue
}
}
}
Now all that's left to do is create your view and pass in the binding. The implementation details are opaque to the View since it is managed by the ViewModel.
struct EditView: View {
#StateObject var vm = EditViewModel()
var body: some View {
Form {
ColorPicker("Color", selection: $vm.color)
}
}
}
If you need even more control, you can additionally compare the last committed color to the new value and only commit if there is a significant change.
I will also agree that a data-driven approach like SwiftUI's can make undo more tricky. For example, when you need to coalesce multiple operations together into one undo group. By its very nature an undo group is procedural-- the user did one thing after the other, and finally terminates on some condition. But I'm sure there is some way to encapsulate this step-wise operation in a transaction object of some kind.
Undo is hard!

CaKeyframeAnimation persisting in background after dismissing view controller

Hi I have simple CAShapeLayer animation
let ballFrameAnimation = CAKeyframeAnimation()
aBall.removeFromSuperlayer()
self.view.layer.addSublayer(aBall)
ballFrameAnimation.keyPath = "position"
ballFrameAnimation.duration = 3.0
ballFrameAnimation.calculationMode = kCAAnimationDiscrete
ballFrameAnimation.fillMode = kCAFillModeForwards
ballFrameAnimation.isRemovedOnCompletion = true
ballFrameAnimation.delegate = self
with a callback to keeps it repeating
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
let name = anim.value(forKey: "name") as? String
if name == "form" {
attachAnimation()
forwardDirection = !forwardDirection
}
However this persists and loops in the background ( at high speed) even when I pop the viewcontroller off the stack. It also shows a memory leak as well on instruments.
#IBAction func unwindDismiss(_ sender: Any) {
aBall.removeAnimation(forKey: "ballAnimation")
aBall.removeFromSuperlayer()
navigationController?.popToRootViewController(animated:true)
dismiss(animated: true, completion: nil)
}
It was simple.
The endless loop animation would not be released by simply dismissing the viewcontroller so it persisted in the background.
By adding a
if navigationController?.topViewController == self {
loop check within the animation did stop cycling.
this fixed not only the accelerating background loops but also the memory leaks. also a warning to fellow NOOBs : when using the instruments repeatedly: close and restart them frequently and also clean builds otherwise you are likely to get bogus leaks.

Multithreaded model building in rails 4 with jRuby

I'm trying to optimize/multi-thread building a very large number of models (300+) all at once to try to speed up the creation this table to be saved to the database in my Rails 4 app.
I tried to move as many references to objects etc outside of the threads with things like memo variables and such, but I'm just not sure what to try anymore.
The code I have is as follows, I tried to keep the code that is being multi-threaded as small as possible but I keep running into circular dependency errors and/or not all of the record are created. Any help is appreciated.
Example 1:
def create
#checklist = Checklist.new(checklist_params)
respond_to do |format|
if #checklist.save
tasks = Task.where(:active => true)
checklist_date_as_time = Time.parse(#checklist.date.to_s).at_beginning_of_day
checklist_id = #checklist.id.to_i
threads = []
ActiveRecord::Base.transaction do
tasks.each do |task|
time = task.start_time
begin
threads << Thread.new do
complete_time = checklist_date_as_time + time.hour.hours + time.min.minutes
task.responses.build( task_start_time: complete_time, must_complete_by: complete_time + task.time_window, checklist_id: checklist_id, task_id: task.id)
end
end while (time += task.frequency.minutes) < task.end_time
threads.map(&:join)
task.save
end
end
format.html { redirect_to #checklist, notice: 'Checklist was successfully created.' }
format.json { render :show, status: :created, location: #checklist }
else
format.html { render :new }
format.json { render json: #checklist.errors, status: :unprocessable_entity }
end
end
AR is not "thread-safe" ... that means that a single record instance's behaviour/correctness when shared between threads is not defined/guaranteed by the framework.
the easiest answer to your question would be to perform the whole tasks = ...; ActiveRecord::Base.transaction do ... work in 1 background thread (frameworks such as DelayedJob might help) - so that the "heavy" computation is not part of the response cycle.
also be aware that using multiple threads might cause you to utilize multiple connections - thus essentially draining the AR pool. it also means that (depending on what's going on during task.responses.build) the desired intention with ActiveRecord::Base.transaction { ... } might not be correct (due multiple connection objects being involved).

Object Deletion Management in javacards

Below, You see a part of Application Programming Notes Java Card 3 Platform Classic Edition about object deletion management :
void updateBuffer(byte requiredSize)
{
try
{
if(buffer != null && buffer.length == requiredSize)
{
//we already have a buffer of required size
return;
}
JCSystem.beginTransaction();
byte[] oldBuffer = buffer;
buffer = new byte[requiredSize];
if (oldBuffer != null)
JCSystem.requestObjectDeletion();
JCSystem.commitTransaction();
}
catch(Exception e)
{
JCSystem.abortTransaction();
}
}
The question is :
When I call JCSystem.requestObjectDeletion(); in the ifexpression, How it recognize which one the buffer or oldBuffer object must be delete?
Object deletion is typically performed during start up. So basically the system can sweep the memory just like a normal Java Garbage Collector at that time.
If no references are found to a specific object, then the space occupied by that object can be collected. As the oldBuffer reference is out of scope by that time there will be no reference to the old array left. The exact memory management is implementation dependent.

Best way to deal with document locking in xPages?

What is the best way to deal with document locking in xPages? Currently we use the standard soft locking and it seems to work fairly well in the Notes client.
In xPages I considered using the "Allow Document Locking" feature but I am worried that people would close the browser without using a close or save button then the lock would never be cleared.
Is there a way to clear the locks when the user has closed his session? I am seeing no such event.
Or is there an easier way to have document locking?
I realize I can clear the locks using an agent but when to run it? I would think sometime a night then I am fairly certain the lock should no longer really be active.
Here is code I'm using:
/* DOCUMENT LOCKING */
/*
use the global object "documentLocking" with:
.lock(doc) -> locks a document
.unlock(doc) -> unlocks a document
.isLocked(doc) -> returns true/false
.lockedBy(doc) -> returns name of lock holder
.lockedDT(doc) -> returns datetime stamp of lock
*/
function ynDocumentLocking() {
/*
a lock is an entry in the application scope
with key = "$ynlock_"+UNID
containing an array with
(0) = username of lock holder
(1) = timestamp of lock
*/
var lockMaxAge = 60 * 120; // in seconds, default 120 min
this.getUNID = function(v) {
if (!v) return null;
if (typeof v == "NotesXspDocument") return v.getDocument().getUniversalID();
if (typeof v == "string") return v;
return v.getUniversalID();
}
/* puts a lock into application scope */
this.lock = function(doc:NotesDocument) {
var a = new Array(1);
a[0] = #UserName();
a[1] = #Now();
applicationScope.put("$ynlock_"+this.getUNID(doc), a);
// print("SET LOCK "+"$ynlock_"+doc.getUniversalID()+" / "+a[0]+" / "+a[1]);
}
/* removes a lock from the application scope */
this.unlock = function(doc:NotesDocument) {
applicationScope.put("$ynlock_"+this.getUNID(doc), null);
//print("REMOVED LOCK for "+"$ynlock_"+doc.getUniversalID());
}
this.isLocked = function(doc:NotesDocument) {
try {
//print("ISLOCKED for "+"$ynlock_"+doc.getUniversalID());
// check how old the lock is
var v = applicationScope.get("$ynlock_"+this.getUNID(doc));
if (!v) {
//print("no lock found -> return false");
return false;
}
// if lock holder is the current user, treat as not locked
if (v[0] == #UserName()) {
//print("lock holder = user -> not locked");
return false;
}
var dLock:NotesDateTime = session.createDateTime(v[1]);
var dNow:NotesDateTime = session.createDateTime(#Now());
// diff is in seconds
//print("time diff="+dNow.timeDifference(dLock)+" dLock="+v[1]+" now="+#Now());
// if diff > x seconds then remove lock, it not locked
if (dNow.timeDifference(dLock) > lockMaxAge) {
// print("LOCK is older than maxAge "+lockMaxAge+" -> returning false");
return false;
}
//print("return true");
return true;
// TODO: check how old the lock is
} catch (e) {
print("ynDocumentLocking.isLocked: "+e);
}
}
this.lockedBy = function(doc:NotesDocument) {
try {
var v = applicationScope.get("$ynlock_"+this.getUNID(doc));
if (!v) return "";
//print("ISLOCKEDBY "+"$ynlock_"+doc.getUniversalID()+" = "+v[0]);
return v[0];
} catch (e) {
print("ynDocumentLocking.isLockedBy: "+e);
}
}
this.lockedDT = function(doc:NotesDocument) {
try {
var v = applicationScope.get("$ynlock_"+this.getUNID(doc));
if (!v) return "";
return v[1];
} catch (e) {
print("ynDocumentLocking.isLockedBy: "+e);
}
}
}
var documentLocking = new ynDocumentLocking();
You could take a page from the way webDAV works. There a servlet manages a "lock-list" of locked documents. The locks automatically expire after 10 minutes. Locks can be renewed or terminated trough calls. So when you edit a document you would request a lock, then kick off a CSJS timer that calls the relocking function every 8 minutes (so you have some margin for error) and the postSave calls the unlock (unless you stay in edit mode).
If a user closes the browser after 10 minutes the document is automatically unlocked. Since you are free how to implement the locking function, you can capture user/location and use that information in the "lock failed" display (you event could push that further and let the original author know about it or do some "retry" option.
It isn't simple to implement, but once implemented simple to use
ApplicationScope may be a good place to capture "locked" documents. After all, for applicationScope to expire, all users' sessions have to have expired, so anyone with the page open will not be able to save anyway.
Maybe capture UNID, user and time when someone edits a doc. Clear the value when the document is saved. Bear in mind that the user might close the browser etc. I've been discussing this approach internally and if we end up building this I would look to add it to OpenNTF. But we're unlikely to get onto it within the next month.
I Prefer to use a solution similar to Mr. Withers' answer. The main issue is how to deal with the unwanted and dreaded back button. It is easy to lock a document when it is opened, but there are many ways to close the XPage, and the user is not limited to just the navigation you provide but also can, as he stated, close the browser completely, use the back button, etc. So, the best way that I can think of is to create a few java objects which we will use in the application and session scopes.
The first step is to create a "LockedDocument" class. As we know, the documents are not serializable and we do not want to save the document itself in this object, we want to save the UNID and the time it was saved. We want to do it this way so that we can manage to clear the object after a given time (like thirty minutes to an hour). This class should also implement the comparable interface in order to sort the collection by this time so that the oldest documents are first and the newest documents are last.
Next we create another class that holds a list or a map with these LockedDocuments. This class must also have a thread (implement Runnable) that will check all documents every five minutes or so, I did not test this yet, but it should work). Any document that was locked thirty to sixty minutes ago (predefined) will be unlocked (deleted from the list). It is important that the list be sorted as described above and that the loop is "broken" when a time less than the locktime is reached in order to prevent unwanted processing.
The next step would be to include the user specific list in the sessionScope. This list is the LockedDocuments that this current user has. It is set when the user changes the document's status to editable, and is checked before the document is set to editable to prevent one document from being opened in multiple tabs by the same user. The lock is once again checked onquerysave(). Once a main page is opened, the lock is automatically released. The onquerysave() must also check to make sure the documents UNID is in the sessionScope list, or if the document is new before allowing a save.
quick recap
Any UNID saved in the applicationScope LockedDocumentList would not be editable by anyone unless it exists in their own sessionScope list.
It is possible to warn a user that their lockedTime is approaching and reset the timer.
The class containing a list with the locked documents must be a singleton
There are probably ways to improve this answer, and I am sure I am missing something. It is just a thought.
There might be a better way to handle this, but it is the best I found.
You can remove the Domino lock in window.onunload event:
window.onunload = function(){
dojo.xhrGet(...
}
No need to reinvent the wheel.

Resources