JDK-8089049 : [WebView] zooming google maps hangs
  • Type: Bug
  • Component: javafx
  • Sub-Component: web
  • Affected Version: 8u40,9
  • Priority: P3
  • Status: Closed
  • Resolution: Cannot Reproduce
  • Submitted: 2015-06-09
  • Updated: 2016-06-08
  • Resolved: 2016-06-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9Resolved
Related Reports
Duplicate :  
Relates :  
Description
Load http://maps.google.com into WebView, try to zoom in/out intensively. The app may hang for a while, up to 30 seconds. Then it returns back to normal.

Also, sometimes "outstanding resource locks detected" message is printed to the console.
Comments
I can easily reproduce this with 8u66, so raising it back to P3. However, it doesn't happen with 8u102-ea builds or with the latest 8u-dev or 9-dev build. This is likely due to the fix for JDK-8153148. I think this bug can be closed as "Cannot reproduce".
08-06-2016

I tested this issue extensively in windows 64 bit with jdk 9 (webkit upgrade) with "-Dprism.poolstats=true -Dprism.maxvram=128M ". and im not able to reproduce this issue. Considering the existence of underlying issue and the complexity of this issue, i prefer to get this issue tested by SQE. Also lowering priority of this issue to P4, considering the frequency of reproducibility.
08-06-2016

The workaround suggested above is still relevant. However, I tried to look for some alternatives. One of the alternatives, also suggested above, is to judge if an RTImage object is still referenced after it's rendered. It has two types of references: 1) as a RenderQueue resource (an instance of the java Ref class, peered by its native counterpart - RQRef), 2) as a WebKit resource (an instance of the native RefPtr class). The 1st type of a ref counter is accessible right from an RTImage instance. It's easy to check whether it is referenced from somewhere else in the RenderQueue. But the 2nd type of a ref counter is not tracked from Java. Moreover, it can happen that a resource is still held by WebKit during a render cycle. WebKit can free it (for instance - recreate, as it does in BitmapTextureImageBuffer::didReset(), for instance) on the next update cycle (and in that time it willl be freed via destructing it as RefPtr referent and then as an RQRef referent which will call the java Ref.deref method and resource disposal). Another obstacle is that an RTImage is rendered on the Render thread but WebKit operates on the FX App thread, and so an object referenced from WebKit cannot be accessed from the RenderThread w/o additional synchronization. Another alternative which I tried is splitting of a RenderQueue into multiple instances, during its filling from WebPage,updateContent(), when it's detected that a part of the queue consumes too many video resources (say, creates too many RTImage objects of a large size in total). Then, rendering of the parts is spread across multiple Render thread passes, in order to let the resources be freed in between. I created a draft of the fix, but it didn't seem reliable to me. For instance, I wasn't able to get re-render requests steadily satisfied by FX. Also, the approach itself brings too much mess in the overall WebView rendering logic. So, it didn't seem perspective to me... My last idea is to go deeper into the google maps logic and the way WebKit implements it. It uses tiles (and the TextureMapper native classes) to back the map images. When the map is zoomed, the images are scaled to some extent, then replaced with images of higher resolution. So, I would try to investigate the exact reason why google maps needs that huge number of images to be created during zooming. Probably, we could optimize it somehow on the native level...
06-08-2015

I did a first pass, and this is a somewhat risky change, so I'll need more time to review and test it.
10-06-2015

Thus, possibly good solution would be to change the resources release logic, making it more flexible, letting resources to be released immediately during a render pass. The WebView synchronization scheme should also be taken into account as it can be broken. This doesn't seem trivial to me... So, I suggest a workaround until we implement it that way. The idea is to identify "fat" raw resources (textures) created during a render cycle and in case of vram pool exhaustion - drop those resources not touching their high-level wrappers referenced from the WebView render queue. I mean RTImage objects. The object itself is treated as a graphics resource referenced from the render queue (the object is mapped to a native peer - ImageBufferJava). It's backed by a permanent texture created on demand. What we can do is to track all that RTImage's textures created during a single render pass, and keep them as "ballast" for the case of vram exhaustion when we will immediately (on the Render thread) drop it. Because we won't touch RTImage object itself (extending a Ref class, a Java peer of the native RQRef), we won't interfere in the main asynchronous release process. The object will lose its data. In case it's life cycle ends with the render pass, this won't be a problem for us (we simply release its texture ahead). This may appear though as an empty (transparent) rect rendered later if the object is retained by WebKit for further usages and if WebKit won't update it (in which case the texture will be recreated and used as usual). Theoretically, this scenario can happen if WebKit creates a canvas and then immediately (in the same render cycle or otherwise the scenario is not reproduced) starts to perform greedy resources allocation up to vram exhaustion. In this case the canvas data will be lost. webrev: http://cr.openjdk.java.net/~ant/RT-46191/webrev.0 RTImage.java: A new texture is added to the tracker as a DisposableResource. WCRenderQueue.java: The disposable tracker is triggered here for disposal in case of emergency. WCGraphicsManager.java: The tracker and the specific exception are defined here. GraphicsDecoder.java: This is where I encountered problems with internal implicit (from WebView's perspective) allocation of resources by Prism. I can't control it. Prism doesn't check for null textures (well, I understand why) and so I can get NPE in case we're close to the pool limit and Prism allocates something internally. So, the only I can do here is to check for some space available, taking into account the GC render target size (which is a toplevel WebView double buffer, of WebView size). PrismGraphicsManager.java: Is where the checkResourcesAvailable(WCGraphicsContext gc) is implemented. WebPage.java: A root of the render cycle. Here I additionally run emergency disposal, request repaint-all for a fresh update of the last frame if necessary and reset the tracker. PrismInvoker.java: The invoker methods are slightly refactored here. The functional change is the following. invokeOnRenderThread - the new method which allows to run a job immediately when we're on the Render thread, otherwise - post. This method is now called from RTImage.dispose() to let the texture be released immediately when called from the Render thread. There's no need to do that asynchronously as before. This shouldn't cause problems. All the rest callers of the Invoker functionally didn't change. And the rest changes introduce checks for null texture or checks for space available (when a texture is created implicitly by prism). Either the exception is thrown, or is just logged (with silent return) when the job happens out of the main render cycle. I tested the fix extensively on all the three platforms. With default prism.maxvram=512M and with prism.maxvram=[256M|128M|1024M]. The result is: no more hangs, no more outstanding locks. However, there're some blinkings (at vram peaks), but I always see a fully rendered map at the end of zoom (so I didn't see remaining artifacts). Still, this looks much better in terms of user experience (30 sec hangs made it very poor). Also, I checked that canvas still works (http://www.w3schools.com/html/html5_canvas.asp), tested with http://maps.yandex.ru and tested with some ordinary resources. All worked fine for me.
09-06-2015

Ideally, we should release a resource as soon as it is no longer needed. Some RTImage objects are used only once (first candidates for immediate release), some are retained by WebKit for further usage (such as canvas and potentially not only). On the native side a graphics resource is managed by two smart pointers: 1) RQRef - Is a WebView render queue ref counter. 2) RefPtr - Is a general purpose WebKit ref counter. First one is used to reference an object in the render queue. It has a mapping into Java structures, so on the java side the ref counting info is accessible. Second one is used to retain and access an object on the native level. This info is not accessible on the Java side. For an object to be disposed (such as RTImage and its backing texture), its RQRef counter should be zero. The counter is increased once an object is put into the render queue and is decreased on its destruction. Objects referenced from the render queue is removed from the queue after its decoding on the Render thread. The removal is posted to Event thread. An object is destructed in case 1) it's no longer referenced from the render queue 2) it's not referenced from anywhere else inside WebKit. That is, its RefPtr counter is zero. The latter, as I said, is not visible from the Java side. And that's the problem (at least). We can not check if an object is eligible for release while we're decoding the render queue on the Java side. So, we should wait the whole render cycle and only then we can release the resources, asynchronously (following to the WebView synchronisation scheme currently in effect).
09-06-2015

The problem is this. Zooming google maps produces lots of images. Every image contains some cartography painting as well. For such image WebKit requests a buffer which can serve a render target. The buffered image is represented by ImageBufferJava.cpp and RTImage.java on the native and java sides respectively. The image itself can be a persistent object on the WebKit side (for instance, an html5 canvas is backed by it) and so its life cycle is managed by WebKit. That is, on the java side the image is backed by a permanent texture: RTImage.txt. This means that a texture can not be disposed until its dispose method is called explicitly. And the problem is that all the graphics resources, which are referenced from the WebView render queue, are released asynchronously, not during a render pass. So, in case of google maps, all the new images which quickly consume vram are retained during the whole render cycle. At some moment, when the prism resource pool is exhausted, no more textures can be created. That is, an attempt to create a texture likely leads to an NPE which is silently swallowed. Then another render op ends up the same way and so on. This keeps going until the whole render queue is completely decoded. This causes the hang.
09-06-2015

-Dprism.poolstats=true shows that vram pool gets exhausted quite quickly as I'm starting to zoom. By default, vram max size is set to 512M for Prism. The problem is easily reproducible on Mac with Retina display due to the scale factor (4 times bigger space is consumed). And it may take some time to appear on Windows/Linux with default settings. However, reducing vram size rises the chances up to 100% when it goes below 256M: -Dprism.maxvram=256M.
09-06-2015