$30 off During Our Annual Pro Sale. View Details »

Optimising Eclipse Plug-ins

alblue
October 27, 2016

Optimising Eclipse Plug-ins

An overview of profiling and optimisation tools that can be used to optimise Eclipse plug-ins, along with sample code and configurations that can be used to make Eclipse faster. Presented at EclipseCon Europe 2016 in Ludwigsburg, Germany. A screencast recording of this is available at https://vimeo.com/alblue/optimising-eclipse-plug-ins
Creative

alblue

October 27, 2016
Tweet

More Decks by alblue

Other Decks in Technology

Transcript

  1. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Optimising Eclipse Plug-ins
    Alex Blewitt
    @alblue
    https://alblue.bandlem.com

    View Slide

  2. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    –Donald Knuth
    “Optimisation is the root of all evil”

    View Slide

  3. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    –Donald Knuth
    “Optimisation is the root of all evil”

    View Slide

  4. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Optimising
    Memory
    CPU
    Code
    size
    Latency
    http://www.brendangregg.com/usemethod.html
    USE method
    * Utilisation
    * Saturation
    * Errors

    View Slide

  5. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flight Recorder
    • Oracle's addition to OpenJDK to allow high-performance recording
    • Commercial feature, payable in production
    • -XX:+UnlockCommericalFeatures
    • -XX:+FlightRecorder
    • Writes out timestamped events to .jfr file
    • Selectively enable events with default.jfc or profile.jfc

    View Slide

  6. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Mission Control
    • Bundled with Oracle JDK in order to read .jfr files
    • Built on Eclipse, or can be installed into an existing Eclipse
    • Can trigger flight recording from an existing process
    • Can connect to non-Oracle JVMs for MBean overview
    • Run with jmc from JAVA_HOME
    http://download.oracle.com/technology/products/missioncontrol/updatesites/base/5.5.0/eclipse/

    View Slide

  7. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Mission Control
    Usage in
    high water
    mark

    View Slide

  8. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Mission Control
    Install Apache Aries
    JMX and register
    PlatformMBeanServer
    for osgi.core beans

    View Slide

  9. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Mission Control
    Press
    once for
    snapshot,
    twice for
    delta
    Different
    pool sizes

    View Slide

  10. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Mission Control
    Updates
    once per
    second

    View Slide

  11. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Mission Control
    Different colour
    icons indicate how
    much presure it
    causes the VM

    View Slide

  12. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JCmd
    • Command line tool for diagnostic commands, uses Java ID (pid)
    • jcmd - show a list of all Java processes on the local host
    • jcmd 1234 help - shows a list of commands or help for cmds
    • jcmd 1234 GC.run - runs the garbage collector System.gc()
    • jcmd 1234 GC.heap_dump - generate hprof heap dump
    • jcmd 1234 GC.class_histogram - dump the heap histogram
    • jcmd 1234 Thread.print - take a stack dump
    Replacement for
    jps, jstat, jmap etc.

    View Slide

  13. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flight Recorder
    • Can record from startup, for a specific time, or on demand
    • -XX:StartFlightRecording=

    settings=profile | default | /path/to/something.jfc

    filename=/path/to/out.jfr

    [ dumponexit=false | true ]

    [ duration=0 | 60s | 10m | 5h | 3d ... ]

    [ delay=0 | 60s | 10m | 5h | 3d ... ]

    [ maxage=0 | 60s | 10m | 5h | 3d ...]

    [ maxsize=0 | 1k | 2m | 3g ... ]
    Default settings are loaded from

    JAVA_HOME/jre/ib/jfr
    -XX:+UnlockCommercialFeatures -XX:StartFlightRecording=

    settings=/tmp/EclipseCon.jfc,dumponexit=true,filename=/tmp/neon.jfr

    View Slide

  14. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flight Recorder Settings
    • Saved as an XML file
    Thread dump
    period must
    be >= 10ms

    View Slide

  15. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    EclipseCon.jfc




    true 500 ms


    true 100 ms


    true 100 ms


    true true


    true 10 ms



    Path allows
    subset of events
    to be captured
    Can also
    specify
    whether to
    capture
    stackTrace or
    in some cases
    threshold

    View Slide

  16. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Viewing a JFR file
    Make sure
    synchronise
    selection is
    enabled
    Ensure event
    types are
    selected from
    Events page
    Shows number of
    events by
    producer
    Shows events
    filtered by event
    types (not all
    captured)
    This is a capture
    from an Eclipse
    4.6 application
    startup and
    shutdown

    View Slide

  17. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    CPU utilisation
    ?
    Nothing
    seems to be
    happening
    here ...
    Rest of program
    never reaches 100%
    CPU
    Splash
    appears
    Eclipse
    running
    Shutdown

    View Slide

  18. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Zooming in
    Drag to zoom
    in events bar
    Or drag in the
    CPU usage

    View Slide

  19. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Found the culprit
    496259

    View Slide

  20. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Profiling with YourKit Thanks to YourKit
    for providing me with
    a license for this
    presentation

    View Slide

  21. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    GC analysis with Censum
    Thanks to JClarity
    for providing me with
    a license for this
    presentation

    View Slide

  22. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Concurrency
    Most
    class
    loads are
    on main
    thread
    Loading 10,000
    takes time, even if
    class isn't complicated -
    reducing classes will
    speed this up

    View Slide

  23. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Concurrency
    Most
    class
    loads are
    on main
    thread

    View Slide

  24. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Problems seen so far
    • Massively single threaded operation with main thread
    • Activator, IStartup, (and next, Workbench.initialize)
    • Limited use of Declarative Services (Component Resolve Thread)
    • Delay with DNS lookup at start, quiet point in CPU utilisation mid-way
    • No observable memory issues at this point (GC looks fine)
    • Where are we spending execution time?

    View Slide

  25. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    • Events can be generated programmatically com.oracle.jrockit.jfr
    • Subclass of InstantEvent, DurationEvent or TimedEvent
    • Events can have associated metadata, and capture stack/thread
    • Associated with a producer and a path for selective enablement
    • Events have a start(), end() and are dumped with commit()
    • No way of reporting a specific time; start/end automatic
    • Example: register a bundle listener to find when activator starts/stops
    Renamed to
    jdk.jfr or
    jdk.internal.jfr
    in Java 9

    View Slide

  26. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    DNS
    delay
    No classloading
    or activator?
    Startup complete
    We can
    record
    custom
    events, like
    timing for
    activators

    View Slide

  27. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    DNS
    delay
    No classloading
    or activator?
    Startup complete
    We can
    record
    custom
    events, like
    timing for
    activators

    View Slide

  28. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events

    View Slide

  29. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    Device.getFontList()
    Workbench.initializeFonts()
    FontRegistry.getFontData()
    ½ second 22 fonts

    View Slide

  30. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events

    View Slide

  31. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events

    View Slide

  32. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events

    View Slide

  33. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flame Graphs
    • Brendan Gregg's Flame Graph generator
    • Fn;NestedFn;NestedFn time - generate from JFR file
    • Convert with flamegraph.pl < neon.flame > neon.svg
    eclipse;org.eclipse.equinox.common 12354587
    eclipse;org.eclipse.equinox.ds 29975629
    eclipse;org.eclipse.equinox.util 5484913
    eclipse;org.eclipse.e4.core.di 46453
    eclipse;org.eclipse.e4.core.di.extensions 38247
    eclipse;org.eclipse.equinox.event 390380
    eclipse;org.eclipse.ui.trace;org.eclipse.core.runtime;org.eclipse.core.contenttype;org.eclipse.equinox.preferences 16893735
    eclipse;org.eclipse.ui.trace;org.eclipse.core.runtime;org.eclipse.core.contenttype;org.eclipse.equinox.registry 64136326
    eclipse;org.eclipse.ui.trace;org.eclipse.core.runtime;org.eclipse.core.contenttype 6180006
    eclipse;org.eclipse.ui.trace;org.eclipse.core.runtime;org.eclipse.core.jobs 11617533
    eclipse;org.eclipse.ui.trace;org.eclipse.core.runtime;org.eclipse.equinox.app 31330246
    eclipse;org.eclipse.ui.trace;org.eclipse.core.runtime 15737669
    eclipse;org.eclipse.ui.trace 6264739
    eclipse;org.eclipse.update.configurator 58846819
    https://github.com/brendangregg/FlameGraph

    View Slide

  34. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flame Graphs
    • Left-to-right order unimportant
    • Relative width shows time and nesting shows values

    View Slide

  35. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flame Graphs
    • Can interactively navigate SVG graph
    • Can search for specific types

    View Slide

  36. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flame Graphs
    • Can also intermix other types e.g. class load
    • See which class loader triggered bundle startup

    View Slide

  37. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Plug-in optimisations
    • Declarative Services solves most problems
    • Avoids the need for BundleActivator classes
    • Avoids the need for IStartup implementations
    • Avoids the need for ServiceTracker
    • ServiceTracker.open() is a blocking call
    • ServiceTracker.open() results in DS activating services

    View Slide

  38. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Plug-in optimisations
    • Declarative Services has some issues to note
    • Methods are called using reflection rather than interface type
    • class.getDeclaredMethods() results in many class loads
    • All argument, return types, and exceptions are loaded
    • Including traversing the inheritance hierarchy
    • (Spring has the same problem)

    View Slide

  39. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Plug-in optimisations
    • Declarative Services has some issues to note
    • Methods are called using reflection rather than interface type
    • class.getDeclaredMethods() results in many class loads
    • All argument, return types, and exceptions are loaded
    • Including traversing the inheritance hierarchy
    • (Spring has the same problem)
    @Component(immediate=true)
    public class MyService implements MyInterface {
    @Activate
    public void activate() {
    // starting things here
    }
    private X doSomethingWith(Y y) throws Z {
    // unrelated method in the same class
    // DS calling 'activate' will load X, Y, Z
    }
    @Override
    public void myServiceMethod() {
    }
    }
    PDE can now
    generate service
    components for
    a project
    Project > Plug-in
    Development >
    DS Annotations
    Activating will
    load (but not
    initialize) X, Y, Z
    And any
    supertypes as
    well

    View Slide

  40. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Best practices
    • Use a separate interface from the implementation
    • Preferably in separate bundles - that way client is decoupled
    • Use (declarative) services to wire components together
    • Use Require-Capability: osgi-extender to require DS
    • Prefer Import-Package to Require-Bundle
    • Activator/IStartup/ServiceTracker -> DS immediate components

    View Slide

  41. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Reducing memory usage
    • Eclipse MAT can be used to find where the objects come from

    View Slide

  42. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Reducing memory usage
    • Platform references
    String
    String +
    char[]

    View Slide

  43. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    URLs are bad!
    • Incredibly inefficient way of storing data
    • "http://eclipsecon.org" - 88 bytes
    • new URL("http://eclipsecon.org") - 224+ bytes - 3x!
    • As if that wasn't bad enough, there is a DNS lookup in hashCode!
    • URL.hashCode() ->
    • URLStreamHandler.hashCode(url) ->
    • InetAddress.getByName(url.getHost())
    URIs are actually worse
    472 bytes in this
    example

    View Slide

  44. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Object Layout
    • jol is an OpenJDK tool that can show
    • internals – how the object is laid out
    • footprint – how much memory it takes
    $ java -jar jol-cli.jar internals java.lang.String
    java.lang.String object internals:
    OFFSET SIZE TYPE DESCRIPTION VALUE
    0 4 (object header) 05 00 00 00
    4 4 (object header) 00 00 00 00
    8 4 (object header) c2 02 00 f8
    12 4 char[] String.value []
    16 4 int String.hash 0
    20 4 (loss due to the next object alignment)
    org.openjdk.jol
    jol-cli
    0.6
    jol-cli-0.6-full.jar

    View Slide

  45. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Object Layout
    • jol is an OpenJDK tool that can show
    • internals – how the object is laid out
    • footprint – how much memory it takes
    $ java -jar jol-cli.jar footprint java.lang.String
    java.lang.String@6842775dd footprint:
    COUNT AVG SUM DESCRIPTION
    1 16 16 [C
    1 24 24 java.lang.String
    2 40 (total)
    org.openjdk.jol
    jol-cli
    0.6
    jol-cli-0.6-full.jar

    View Slide

  46. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Java Object Layout
    • Uses the default constructor of the class being tested
    • If the class has no (sensible) default constructor
    • Create a wrapper class
    • In default constructor, instantiate/store type
    • Run jol-cli.jar footprint on wrapper class

    View Slide

  47. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    • Uses the default constructor of the class being tested
    • If the class has no (sensible) default constructor
    • Create a wrapper class
    • In default constructor, instantiate/store type
    • Run jol-cli.jar footprint on wrapper class
    Java Object Layout
    public class Wrapper {
    public final URI uri;
    public Wrapper() throws Exception {
    uri = new URI("http://eclipsecon.org");
    }
    }
    $ java -cp jol-cli.jar:. org.openjdk.jol.Main footprint Wrapper
    Wrapper@574caa3fd footprint:
    COUNT AVG SUM DESCRIPTION
    1 16 16 Wrapper
    6 41 248 [C
    6 24 144 java.lang.String
    1 80 80 java.net.URI
    14 488 (total)
    488 – 16 = 472

    View Slide

  48. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Suggested fixes
    • Fix URLImageDescriptor to store String instead of URL
    • Convert to a URL on demand for e.g. stream contents
    • Most uses won't need the additional URL fields
    • Fix the rampant layer violation in MenuHelper as well ...
    • If you must use URLs (e.g. through API contracts) then cache
    • P2 URL cache fixed 442915

    View Slide

  49. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Primitive wrapper classes
    • There are still a non-trivial number of wrapper creations
    • new Boolean(true) -> Boolean.TRUE
    • new Boolean(x) -> Boolean.valueOf(x)
    • Smaller code footprint
    • new Boolean(x) - 8 bytes
    • Boolean.valueOf(x) - 3 bytes
    • Using transformer agent shows few k savings in JDT
    489706
    489697
    491825
    476814
    476724
    Constructors are
    deprecated in
    Java 9

    View Slide

  50. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Memory and GC
    • Java's garbage collection is best suited for
    • Short lived objects, which get GC'd quickly
    • Long lived objects, which stay around for ever
    • Different GC strategies have changed over years
    • Some Eclipse code is written for JVMs before J2SE
    • Allocation is not as 'expensive' as it used to be

    View Slide

  51. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Generational GC
    Survivor
    0
    Survivor
    1
    Eden Tenured
    Young Generation Old Generation
    Minor GC Major/Full GC
    Survivor space is
    copied and
    flipped on each
    minor GC
    Tenured objects
    are promoted at
    tenuring threshold
    Objects are
    created in Eden
    PermGen
    Meta
    space

    View Slide

  52. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JVisual VM
    • Provides a view into a JVM's state
    • Run jvisualvm from JAVA_HOME
    • Connects to local or remote JVMs
    • Install Tools > Plugins > Visual GC for more
    • Present in OracleJDK but can connect to non-OracleJDKs

    View Slide

  53. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JVisual VM
    (max, current):
    occupancy
    Young gen
    column
    Old gen
    column
    Off heap
    column
    Slope shows
    allocation rate
    Minor GCs

    View Slide

  54. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    G1 GC
    Tenured Tenured H ug e
    Tenured
    Eden Survivor
    Survivor Tenured
    Eden Tenured
    -XX:+UseG1GC
    1..32Mb
    2048+ regions

    View Slide

  55. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    G1 GC
    Tenured Tenured H ug e
    Tenured
    Eden Survivor Tenured
    Tenured
    -XX:+UseG1GC
    1..32Mb
    2048+ regions

    View Slide

  56. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    G1 GC
    Tenured Tenured H ug e
    Tenured Tenured
    Eden Survivor
    -XX:+UseG1GC
    1..32Mb
    2048+ regions

    View Slide

  57. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    12 bytes 4 8
    12 bytes 4 4 4
    Header A, l, e, x
    length
    Header *char[] hash pad
    Strings in Java
    String
    char[]
    "Alex"
    24 bytes
    24 bytes
    48 bytes

    View Slide

  58. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Strings in Java
    String
    char[]
    "Alex"

    View Slide

  59. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Strings in Java
    String
    char[]
    "Alex"
    String String String String String
    String
    "Alex" "Alex" "Alex" "Alex" "Alex"
    char[] char[] char[] char[] char[]
    "Alex"
    char[]

    View Slide

  60. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Strings in Java
    String
    char[]
    "Alex"
    String String String String String
    "Alex" "Alex" "Alex" "Alex" "Alex"
    char[] char[] char[] char[] char[]
    -XX:+UseG1GC -XX:+UseStringDeduplication
    48 bytes 48 bytes 48 bytes 48 bytes 48 bytes 48 bytes
    288 bytes

    View Slide

  61. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Strings in Java
    String
    char[]
    "Alex"
    String String String String String
    "Alex" "Alex" "Alex" "Alex" "Alex"
    -XX:+UseG1GC -XX:+UseStringDeduplication
    48 bytes 24 bytes 24 bytes 24 bytes 24 bytes 24 bytes
    168 bytes
    490341

    View Slide

  62. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    "Alex" "Alex" "Alex" "Alex" "Alex"
    String String String String String
    Strings in Java
    String
    char[]
    "Alex"
    String.intern()
    48 bytes 24 bytes 24 bytes 24 bytes 24 bytes 24 bytes
    168 bytes

    View Slide

  63. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Strings in Java
    String
    char[]
    "Alex"
    String.intern()
    48 bytes

    View Slide

  64. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Strings in Eclipse
    String
    char[]
    "Alex""objectClass"
    String
    char[]
    0
    8512 bytes
    133
    "MenuItem"
    String
    char[]
    10512 bytes
    219
    26880 bytes
    560
    "platform"
    String
    char[]
    36960 bytes
    660
    String
    "false"
    char[]
    42960 bytes
    895
    String
    "true"
    char[]
    48 bytes
    48 bytes
    64 bytes 56 bytes 48 bytes
    intern()
    5280 bytes
    3232 bytes 13464 bytes 15827 bytes 21504 bytes
    -XX+UseDedup

    View Slide

  65. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Suggested fixes
    • Registry and E4 model use many, many, many String values
    • true, false, MenuItem, View, ColorDefinition, defaultAccelerator
    • Intern String values in implementations in:
    • (registry) ConfigurationElement.propertiesAndValues
    • (css.swt.dom) WidgetElement.localName
    • (e4.model.basic) PartImpl.tags
    Saves 2Mb+

    View Slide

  66. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    String.intern?

    View Slide

  67. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Isn't that an anti-pattern?

    View Slide

  68. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    String.intern()
    • Used to be a problem
    • Strings stored in (write-only) PermGen
    • Couldn't be garbage collected
    • Bucket size was fixed to a small number (1009)
    • Used to be avoided
    6

    View Slide

  69. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    String.intern()
    • These problems have all gone away, since Java 7
    • Strings no longer stored in PermGen (and no more PermGen!)
    • Strings are stored on heap and so can be garbage collected
    • Bucket size increased to 60013 (-XX:StringTableSize=60013)
    • Use it wherever you have large volumes of small Strings
    • -XX:+UseStringDeduplication helps but doesn't solve problem
    7 8

    View Slide

  70. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Performance recommendations
    • Avoid networking lookups or I/O on the main/blocking thread
    • Remove BundleActivator and IStartup code
    • Move startup of services from Activator to Declarative Services Components
    • Migrate data structures from load-at-start to load-on-demand
    • Reduce the number of classes (move inner classes to method references)
    • Avoid using URL or URI to store data, and avoid usage in APIs
    • Use String.intern() for long-lived and limited sized String references
    • Measure with flame graphs or profile with JMH where appropriate

    View Slide

  71. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Go forth and make Eclipse fast!

    View Slide

  72. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Questions

    View Slide

  73. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Appendix

    View Slide

  74. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    • An EclipseActivatorEvent can be used to record activator time
    @EventDefinition(name = "EclipseActivator", description="Eclipse Activator",
    path="eclipse/activator", stacktrace = true, thread = true)
    public class EclipseActivatorEvent extends TimedEvent {
    @ValueDefinition(id = "bundleName", name = "bundleName", description = "Name")
    private String bundleName;
    public String getBundleName() {
    return bundleName;
    }
    public void setBundleName(String bundleName) {
    this.bundleName = bundleName;
    }
    public EclipseActivatorEvent() {
    super(EclipseTokens.EclipseActivatorEventToken);
    } }

    View Slide

  75. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    • Tokens are used by the runtime to associate event types
    public class EclipseTokens {
    private static final String URI = "http://www.eclipsecon.org/2016/jfr/";
    public static final EventToken EclipseActivatorEventToken;
    public static final EventToken EclipseAnotherEventToken;
    static {
    try {
    Producer producer = new Producer("Eclipse", "EclipseCon Demo of JFR", URI);
    EclipseActivatorEventToken= producer.addEvent(EclipseActivatorEvent.class);
    EclipseAnotherEventToken = producer.addEvent(EclipseAnotherEvent.class);
    producer.register();
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    } }
    This is Java 8;
    changes in Java 9
    JFR

    View Slide

  76. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    • Can register a SynchronousBundleListener to measure time
    public final class JFRBundleListener implements SynchronousBundleListener {
    public void bundleChanged(BundleEvent bundleEvent) {
    Bundle bundle = bundleEvent.getBundle();
    int type = bundleEvent.getType();
    EclipseActivatorEvent event = Activator.events[(int) id];
    String name = bundle.getSymbolicName();
    if (type == BundleEvent.STARTING && event != null) {
    event.begin();
    } else if (type == BundleEvent.STARTED && event != null) {
    event.end();
    event.setBundleSymbolicName(name);
    event.commit();
    }
    } }
    Needs to be
    registered/started
    ahead of DS,
    simpleconfigurator
    etc.

    View Slide

  77. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    • Can register a SynchronousBundleListener to measure time
    public class Activator implements BundleActivator {
    static final int MAX_BUNDLES = 1000;
    static EclipseActivatorEvent[] events = new EclipseActivatorEvent[MAX_BUNDLES];
    public void start(BundleContext context) throws Exception {
    for (int i = 0; i < events.length; i++) {
    events[i] = new EclipseActivatorEvent();
    }
    context.addBundleListener(new JFRBundleListener());
    }
    public void stop(BundleContext context) throws Exception {
    }
    }
    Needs to be
    registered/started
    ahead of DS,
    simpleconfigurator
    etc.

    View Slide

  78. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Custom Events
    • Added to JFC for enabling specific event types

    description="Demo of settings for EclipseCon" provider="Alex Blewitt">
    ...>


    true
    0 ms



    View Slide

  79. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flame Graph
    • Generate FlameGraph events from JFR
    private static SortedSet readRecording(File recordingFile) throws IOException {
    TreeSet flameEvents = new TreeSet();
    Parser parser = new Parser(recordingFile);
    for (ChunkParser chunkParser : parser) {
    for (FLREvent event : chunkParser) {
    if ("vm/class/load".equals(event.getPath())) {
    FLRStruct resolvedValue = (FLRStruct) event.getResolvedValue("loadedClass");
    String message = (String) resolvedValue.getResolvedValue("name").toString();
    FlameEvent flameEvent = new FlameEvent(event.getStartTime(), event.getTimestamp(), message);
    flameEvents.add(flameEvent);
    }
    if ("eclipse/activator".equals(event.getPath())) {
    String message = (String) event.getResolvedValue("bundleSymbolicName");
    FlameEvent flameEvent = new FlameEvent(event.getStartTime(), event.getTimestamp(), message);
    flameEvents.add(flameEvent);
    } }
    parser.close();
    return flameEvents;
    } }

    View Slide

  80. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flame Graph
    • Generate FlameGraph events from JFR
    private static void writeFlameEvents(SortedSet flameEvents,
    File outputFile) throws IOException {
    try (FileWriter out = new FileWriter(outputFile)) {
    Stack stacks = new Stack();
    Iterator it = flameEvents.iterator();
    while (it.hasNext()) {
    FlameEvent otherFlameEvent = (FlameEvent) it.next();
    writeBefore(stacks, otherFlameEvent.getStart(), out);
    if (!stacks.isEmpty())
    stacks.peek().addDelta(otherFlameEvent.getDuration());
    stacks.push(otherFlameEvent);
    }
    writeBefore(stacks, Long.MAX_VALUE, out);
    out.flush();
    } }

    View Slide

  81. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flame Graph
    • Generate FlameGraph events from JFR
    private static void write(Stack stack, Writer out) throws IOException
    {
    FlameEvent topEvent = stack.peek();
    long time = topEvent.getAdjustedDuration();
    Iterator it = stack.iterator();
    StringBuffer buffer = new StringBuffer("eclipse");
    while (it.hasNext()) {
    buffer.append(';');
    FlameEvent type = (FlameEvent) it.next();
    buffer.append(type.getMessage());
    }
    buffer.append(" " + time);
    out.write(buffer.toString());
    }

    View Slide

  82. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    Flame Graph
    • Generate FlameGraph events from JFR
    private static void writeBefore(Stack stack, long before,
    Writer out) throws IOException {
    if (!stack.isEmpty() && stack.peek().getEnd() < before) {
    write(stack, out);
    stack.pop();
    writeBefore(stack, before, out);
    }
    }
    public static void main(String[] args) throws IOException {
    File recordingFile = new File(args[0]);
    File outputFile = new File(args[1]);
    SortedSet flameEvents = writeBefore(recordingFile);
    writeFlameEvents(flameEvents, outputFile);
    }

    View Slide

  83. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH
    • Java Microbenchmark Harness
    • Add a @Benchmark annotation to methods under test
    // 18 bytecode instructions
    @Benchmark
    public String chain() {
    return new StringBuffer().append(a).append(b).toString();
    }
    // 24 bytecode instructions
    @Benchmark
    public String sequence() {
    StringBuilder sb = new StringBuilder();
    sb.append(a);
    sb.append(b);
    return sb.toString();
    }

    View Slide

  84. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH
    • Java Microbenchmark Harness
    • Wrap in a @State(Scope.Benchmark) class with @Param
    @State(Scope.Benchmark)
    public class StringBuilderBenchmark {
    @Param({ "Hello", "Goodbye" })
    private String a;
    @Param({ "World", "EclipseCon" })
    private String b;
    @Benchmark
    public String sequence() {...}
    @Benchmark
    public String chain() {...}
    }

    View Slide

  85. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH
    • Java Microbenchmark Harness
    • Build and run -wi 5 -tu ns -f 1 -bm avgt
    # JMH 1.15 (released 22 days ago)
    # VM version: JDK 1.8.0_102, VM 25.102-b14
    # VM invoker: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/bin/java
    Benchmark (a) (b) Mode Cnt Score Error Units
    StringBuilderBenchmark.chain Hello World avgt 20 15.038 ± 0.083 ns/op
    StringBuilderBenchmark.chain Hello EclipseCon avgt 20 15.935 ± 0.129 ns/op
    StringBuilderBenchmark.chain Goodbye World avgt 20 15.380 ± 0.128 ns/op
    StringBuilderBenchmark.chain Goodbye EclipseCon avgt 20 16.574 ± 0.130 ns/op
    StringBuilderBenchmark.sequence Hello World avgt 20 28.993 ± 0.524 ns/op
    StringBuilderBenchmark.sequence Hello EclipseCon avgt 20 28.354 ± 0.262 ns/op
    StringBuilderBenchmark.sequence Goodbye World avgt 20 29.354 ± 0.208 ns/op
    StringBuilderBenchmark.sequence Goodbye EclipseCon avgt 20 50.103 ± 0.582 ns/op
    Average
    time
    Warmups
    Time unit

    View Slide

  86. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH
    • Java Microbenchmark Harness
    • Build and run -wi 5 -tu ns -f 1 -bm avgt -prof gc
    # JMH 1.15 (released 22 days ago)
    # VM version: JDK 1.8.0_102, VM 25.102-b14
    # VM invoker: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/bin/java
    Benchmark (a) (b) Mode Cnt Score Error Units
    ..chain:·gc.alloc.rate.norm Hello World avgt 20 64 ± 0.001 B/op
    ..chain:·gc.alloc.rate.norm Hello EclipseCon avgt 20 72 ± 0.001 B/op
    ..chain:·gc.alloc.rate.norm Goodbye World avgt 20 64 ± 0.001 B/op
    ..chain:·gc.alloc.rate.norm Goodbye EclipseCon avgt 20 80 ± 0.001 B/op
    ..sequence:·gc.alloc.rate.norm Hello World avgt 20 112 ± 0.001 B/op
    ..sequence:·gc.alloc.rate.norm Hello EclipseCon avgt 20 120 ± 0.001 B/op
    ..sequence:·gc.alloc.rate.norm Goodbye World avgt 20 112 ± 0.001 B/op
    ..sequence:·gc.alloc.rate.norm Goodbye EclipseCon avgt 20 216 ± 0.001 B/op
    Average
    time
    Warmups
    Time unit Profile GC
    Other profilers exist,
    like perfasm

    View Slide

  87. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH in Eclipse
    • It is possible to use Eclipse's annotation profiler to test JMH projects

    View Slide

  88. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH in Eclipse
    • Factory path needs jmh-generator-annprocess and jmh-core

    View Slide

  89. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH in Eclipse
    • Turn off warnings in generated code

    View Slide

  90. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH in Eclipse
    • It's possible to configure annotation processing in Eclipse for JMH

    View Slide

  91. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH in Eclipse
    • It's possible to configure annotation processing in Eclipse for JMH

    View Slide

  92. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMH in Eclipse
    • It's possible to configure annotation processing in Eclipse for JMH

    View Slide

  93. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    StringBuffer and StringBuilder
    • StringBuffer is synchronized; StringBuilder is not
    • Chaining is more efficient than sequencing
    • Custom HotSpot code for chained calls -XX:+OptimizeStringConcat
    public static String chain(String a, String b) {
    return new StringBuffer().append(a).append(b).toString();
    } // 18 bytecode instructions
    public static String sequence(String a, String b) {
    StringBuilder sb = new StringBuilder();
    sb.append(a);
    sb.append(b);
    return sb.toString();
    } // 24 bytecode instructions
    https://alblue.bandlem.com/2016/04/jmh-stringbuffer-stringbuilder.html
    492200
    492230

    View Slide

  94. Copyright (c) 2016, Alex Blewitt, Bandlem Ltd
    EclipseCon 2016
    JMX beans in OSGi
    • Supported with Apache Aries JMX
    • Install org.apache.aries.{jmx,util}, org.osgi.service.cm
    // Import-Package: javax.management,org.osgi.framework
    public class Activator implements BundleActivator {
    public void start(BundleContext context) throws Exception {
    MBeanServer pmbs = ManagementFactory.getPlatformMBeanServer();
    context.registerService(MBeanServer.class.getName(), pmbs, null);
    }
    public void stop(BundleContext context) throws Exception {
    }
    }
    Don't forget
    javax.management
    on Equinox

    View Slide