Scaling Ruby Applications with JRuby

F1d37642fdaa1662ff46e4c65731e9ab?s=47 headius
February 07, 2020

Scaling Ruby Applications with JRuby

JRuby is the fastest way to run Ruby apps, from microservices to large monolithic Rails apps, and it’s easier than ever to get started! We’ll cover JRuby installation, migrating apps, and deploying and scaling applications using JRuby and standard hosting services. Save money! Use JRuby!

Delivered Feb 7, 2020 at RubyFuza in Cape Town, South Africa



February 07, 2020


  1. Scaling Ruby with JRuby Thomas E. Enebo (@tom_enebo) Charles O.

    Nutter (@headius)
  2. • JRuby co-leads • Red Hat Charles Thomas Ruby Java

  3. What is JRuby? Ruby Implementation (2.5.7 compatible) …which happens to

    run on the Java Virtual Machine (JVM)
  4. JRuby • Lots of memory improvements • Better support

    for Java module system • .jruby.java_opts file to manage JVM options • Improved startup time for most commands • Dozens of issues fixed • coming out next week? next (2.6.x)
  5. Why JRuby? What are the differences? - JRuby - C

  6. Native Threads vs Global Interpreter Lock

  7. require 'benchmark' ary = (1..1000000).to_a loop { puts Benchmark.measure {

    10.times { ary.each {|i|} } } } Unthreaded require 'benchmark' ary = (1..1000000).to_a loop { puts Benchmark.measure { (1..10).map { { ary.each {|i|} } }.map(&:join) } } Multi-threaded
  8. Ruby 2.5.7 unthreaded JRuby unthreaded Ruby 2.5.7 threaded JRuby threaded

  9. Time (in seconds) 0 0.09 0.18 0.27 0.36 Unthreaded Threaded

    0.15 0.23 0.35 0.34 CRuby JRuby (lower is better)
  10. Virtual Machine vs Going It Alone

  11. Ruby Teams Hiro Marcin Nahi Subbu Douglas Christian Karol Tom

    Charlie Hiro charli Nahi zzak nurse hsbt matz ko1 nobu OS (libc, ...) JRuby MRI
  12. Ruby Teams Hiro Marcin Nahi Subbu Douglas Christian Karol Tom

    Charlie Hiro charli Nahi zzak nurse hsbt matz ko1 nobu OS (libc, ...) JRuby MRI JVM POSIX
  13. JVM Ruby Teams Hiro Marcin Nahi Subbu Douglas Christian Karol

    Tom Charlie Hiro charli Nahi zzak nurse hsbt matz ko1 nobu OS (libc, ...) JRuby MRI Better ??? POSIX
  14. !Perspective! ??? Total control can be great!

  15. Shoulders of Giants JVM J. Rose J. Rose J. Rose

    J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose Hiro Marcin Nahi Subbu Douglas Christian Dmitry Tom Charlie JRuby
  16. All the stuff! JVM J. Rose J. Rose J. Rose

    J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose Garbage Collection Native JIT Profiled Optimizations Native Threading Tooling Cross Platform
  17. Alternate JVMs • Hotspot: standard OpenJDK Java VM • OpenJ9:

    open source version of IBM's J9 JVM • New GC and JIT tuning options • Startup time and JIT code-caching features • GraalVM: replaces Hotspot JIT with Graal JIT • New JIT optimizations, ahead-of-time native compilation
  18. JVM is everywhere++! • Unix++, Windows • Exotic platforms: zLinux,

    OpenVMS, AS/400 • Mobile: Android’s Dalvik, Embedded JVMs
  19. Java bytecode == portability

  20. actionmailer-javamail, active_documentum, activerecord-jdbc-adapter, activerecord-jdbcdbf-adapter, activerecord-jdbcderby-adapter, activerecord-jdbch2-adapter, activerecord-jdbchsqldb-adapter, activerecord-jdbcmssql-adapter, activerecord-jdbcmysql-adapter, activerecord-jdbcpostgresql-adapter,

    activerecord-jdbcsqlite3- adapter, activerecord-netezza-adapter, activerecord-vertica-adapter, akephalos, akephalos-nerian, akephalos2, akka-actor-jars, akka-remote-jars, akubra_llstore_migrate, Antwrap, async-http-client-jars, atomic, atoulme-Antwrap, autotest-java, bbrowning-deltacloud-client, bbrowning-deltacloud-core, bcrypt- ruby, bee_java, berkeley-db-java-jars, bert, bio-maf, blockenspiel, boc, bond, bosdk, bouncy-castle-java, boxed-geminabox, brute-fuzzy, bryanl-gherkin, bson, buby, buildr, buildr-resolver, buildrizpack, butternut, capistrano-java, capybara-java_script_lint, carrierwave-neo4j, carrierwave_imagevoodoo, cassandra-jars, chrest, cloby, commons-io-jars, concurrently, contextual, coupler, cucumber-java, cucumber-jvm, cuke4duke, cuuid, dm-ldap-adapter, dm-lucene-adapter, do-jdbc_sqlserver, do_derby, do_h2, do_hsqldb, do_jdbc, do_mysql, do_openedge, do_oracle, do_postgres, do_sqlite3, do_sqlserver, doubleshot, dripdrop, dubious, duby, engineyard-visualvm, epall-limelight, errbit_zmq_handler, eurydice, euston, euston-daemons, euston-eventstore, euston-projections, euston-rabbitmq, euston- websites, eventmachine, excemel, faye-websocket, ffi, fig, file-find, fishwife, foreman, forkit, forkjoin, gamelan, gemshit, geoip-jars, get_back, gherkin, glassfish, gravitext-util, gravitext-xmlprod, grizzly, guava-jars, hadoop-find, hashdot-test-daemon, hiredis, hitimes, hope, hostor, hot_bunnies, hourglass, hpricot, http_parser.rb, inline_javascript, iudex, iudex-async-httpclient, iudex-barc, iudex-brutefuzzy-protobuf, iudex-brutefuzzy-service, iudex-char-detector, iudex-core, iudex-da, iudex-filter, iudex-html, iudex-http, iudex-http-test, iudex-httpclient-3, iudex-jetty-httpclient, iudex-rome, iudex-simhash, iudex-worker, ivy-jars, iyyov, jactive_support, java-autotest, java-inline, java2ruby-xmldsig, java_bin, java_inline, java_override, java_properties, java_streamify, java_testing_guff, javabean_xml, javaclass, javaeye4r, javagems, javajake, javaobj, javaobjs, javaobs, javaparse, javasand, javascript-securehash-rails, javascript-state-machine-rails, javascript_auto_include, javascript_eraser, javascript_features, javascript_i18n, javascript_localize, javascript_safe_logger, javascript_util_asset_pack, javascripto, javascripto-rails, jdbc-derby, jdbc-hsqldb, jdbc-jtds, jdbc-openedge, jdbc-openedge-internal, jdbc-postgres, jdbc-sqlite3, jedis-jars, jena-jruby, jessica, jettr, jetty, jetty- jsp, jgeoip, jms4r, jpdfer, jrack_handlers, jrtm, jruby-activemq, jruby-akka_jars, jruby-elasticsearch_jars, jruby-httpclient, jruby-launcher, jruby-management, jruby- metrics, jruby-pageant, jruby-vijava, jruby_gc_stats, jruby_sandbox, jruby_threach, jrubyconf-button, jsmetric4java, json, json-jruby, jsound, kb-activerecord-jdbc- adapter, kirk, kyotocabinet-java, ladle, latex-decode, launchy, libnotify, limelight, linecache, logback, logback-jars, looksee, lumix, mack-javascript, markdownj, maven_irb_plugin, metrics-core-jars, metrics-java, mguymon-buildr, mikka, mini_aether, mirah, mirah_model, miso-java, mixology, mm_mq, mongrel, msgpack-idl- java, msgpack-jruby, multimeter, naether, nanoc-javascript-concatenator, neo4j, neo4j-admin, neo4j-advanced, neo4j-community, neo4j-core, neo4j-enterprise, neo4j-spatial, neo4j-will_paginate, neo4j-wrapper, netty-jars, ning-compress-jars, nio4r, nokogiri, nokogiri-fitzsimmons, nokogiri-maven, nosqoop4u, ontomde- demo-java5, ontomde-java, ontomde-java-frontend, ontomde-uml2-java, ontomde-uml2-kbjava, open_nlp, pacer, pacer-dex, pacer-neo4j, pacer-orient, pelops-jars, persvr, pg_array_parser, protobuf-jars, pry, puma, qtjruby-core, qwirk_active_mq_adapter, qwirk_jms_adapter, rabbitmqadmin-cli, ragweed, rails_javascript_helpers, rakejava, rave, rcov, realityforge-jekyll, realityforge-jekylltask, redcar-bundles, redcar-clojure, redcar-filter-through-command, redcar- groovy, redcar-icons, redcar-javamateview, redcar-javascript, redcar-mirah, redcar-svnkit, redcar-xulrunner-win, RedCloth, refinerycms-javascripts, reigns, revo- nokogiri, rika, rjack-async-httpclient, rjack-commons-codec, rjack-commons-dbcp, rjack-commons-dbutils, rjack-commons-pool, rjack-httpclient-3, rjack- httpclient-4, rjack-icu, rjack-jackson, rjack-jdom, rjack-jets3t, rjack-jetty, rjack-jetty-jsp, rjack-jms, rjack-jms-spec, rjack-logback, rjack-lucene, rjack-maven, rjack- mina, rjack-nekohtml, rjack-protobuf, rjack-qpid-client, rjack-rome, rjack-slf4j, rjack-solr, rjack-tarpit, rjack-xerces, rmagick4j, rmodbus, rtm-javatmapi, rtm- majortom, rtm-ontopia, rtm-tinytim, rtm-tmql, rubeus, rubinius-core-api, ruby-blockcache, ruby-debug-base, ruby-maven, ruby2java, rubydoop, rubyjedi- nokogiri_java, scala-inline, scala-library-jars, scriptty, slf4j, slf4j-jars, slyphon-zookeeper, slyphon-zookeeper_jar, smackr, smartimage, SNMP4JR, solr_sail, spiegela- jruby-httpclient, sproutcore, spymemcached, sqldroid, steamcannon-deltacloud-client, steamcannon-deltacloud-core, stilts-stomp-client, supermarket, svm_toolkit, swt, theduke, thick, to-javascript, torquebox-base, torquebox-cache, torquebox-configure, torquebox-container-foundation, torquebox-core, torquebox- messaging, torquebox-messaging-container, torquebox-naming, torquebox-naming-container, torquebox-security, torquebox-server, torquebox-vfs, torquebox- web, twitter4j4r, UDJrb, unageanu-javaclass, unimidi, universe-javascript, url_escape, weakling, webbit-jars, wildnet-jackson, wildnet-netty, wildnet-server, wildsonet-hazelcast, wildsonet-netty, wildsonet-server, wildsonet-streamer, wrest, wrong, xdojava, xqruby, zookeeper No build tools!
  21. Ruby C Extensions vs Java Native Extensions

  22. Why Extensions? • Performance • Access Existing Library (e.g. openssl)

  23. C Extensions • API is massive • API is specific

    to C Ruby’s implementation • It is the implementation • Huge support cost (if non-CRuby wanted to support it) • At odds with concurrent Ruby execution
  24. Java Native Extensions • API is massive • API is

    specific to JRuby’s implementation • It is the implementation* • Allows Concurrent Ruby Execution • Support cost minimal • Common Gems Supported * We have plans for a formal API
  25. oj: Optimized json • Fast json parsing and dumping with

    many options • Common transitive dependency with its own API • Needed for many popular apps/libraries
  26. oj for JRuby! • 10700 lines of Java (vs 20k

    lines of C) • Almost ready: 469 runs, 4883 assertions, 12 failures, 1 errors • 5 date/time differences • Streaming IO missing • No optimization work yet
  27. Load Performance 0M 0.3M 0.6M 0.9M 1.2M small medium large

    0.13 0.29 1.1 0.05 0.11 0.36 0.06 0.11 0.72 MRI (oj) JRuby (json) JRuby (oj) Millions of loads per second (higher is better)
  28. Dump Performance 0 0.75 1.5 2.25 3 small medium large

    0.44 0.86 2.3 0.22 0.44 1.1 0.33 0.73 2.1 MRI (oj) JRuby (json) JRuby (oj) Million of dumps per second (higher is better)
  29. Scripting Java

  30. Scripting Java • Access Java libraries using a Ruby syntax

    • Or Scala, or Clojure, or Kotlin... • Alternatives to existing Ruby libraries • APIs which may not exist in Ruby • More options!
  31. JRuby IRB

  32. Brakeman Pro

  33. Fun Stuff event(:player_egg_throw) do |e| e.hatching = true e.num_hatches =

    120 e.player.mesg "hatched" end Purugin
  34. JRuby Performance

  35. Startup Time • Runtime optimizations give us excellent performance •

    ...eventually! • Startup time, warmup time are impacted • We continue working to reduce this impact • We compare some common commands
  36. JRuby Architecture Ruby (.rb) JIT Java Instructions (java bytecode) Ruby

    Instructions (IR) parse interpret interpreter interpret C1 compile native code better native code java bytecode interpreter execute C2 compile Java Virtual Machine JRuby Internals deoptimize Performance improves the longer your app runs
  37. 0s 1.5s 3s 4.5s 6s gem list 5.025s 0.678s CRuby

    JRuby --dev
  38. JRuby Flag: --dev • export JRUBY_OPTS="--dev" • Disables JRuby's JIT

    • Reduces JVM JIT • Don't use when benchmarking! • 30-40% reduction • More improvements coming total execution time (lower is better) 0s 1.5s 3s 4.5s 6s gem list (200 gems) 3.0s 5.0s JRuby JRuby --dev
  39. 0s 1.75s 3.5s 5.25s 7s rails console 6.7s 4.96s CRuby

    JRuby --dev
  40. OpenJDK Class Data Sharing • Pre-validate and cache class data

    • Combined with --dev gives best current JRuby startup •
  41. Java 13 total execution time (lower is better) 0s 1.5s

    3s 4.5s 6s gem list (200 gems) 2.84 3.358 5.589s 2.962s 5.025s JRuby JRuby --dev JRuby --dev --dev + CDS
  42. App Performance • Sinatra and Roda • • Comparing

    CRuby, JRuby, and TruffleRuby • RedMine bug tracker and wiki • • Real-world Rails application on JRuby, CRuby
  43. Peak Performance • After initial startup, warmup, data caching •

    JRuby generally gives better peak performance • Be aware of warmup time
  44. Sinatra and Roda requests/second (higher is better) 0 12500 25000

    37500 50000 Sinatra Roda 5,846 4,257 44,703 42,468 14,489 12,284 CRuby JRuby TruffleRuby
  45. Redmine Issue View, rendered and json (higher is better) 0

    req/s 35 req/s 70 req/s 105 req/s 140 req/s issues/13 issues/13.json 137 req/s 50 req/s 91 req/s 41 req/s CRuby 2.6.2 JRuby 9.2.9
  46. Warmup Time • Large applications take longer to warm up

    • Working on new JIT metrics, profiling to reduce this curve • Ahead-of-time compile to JVM bytecode should help
  47. Sinatra performance over time, requests/second 0rps 12500rps 25000rps 37500rps 50000rps

    5s 10s 15s 20s 25s CRuby JRuby
  48. Roda performance over time, requests/second 0rps 12500rps 25000rps 37500rps 50000rps

    5s 10s 15s 20s 25s CRuby JRuby
  49. Redmine issues/13.json warmup, one-minute cycles 0 35 70 105 140

    1st 2nd 3rd 4th 5th 6th CRuby 2.6.2 JRuby 9.2.9
  50. Redmine memory usage (lower is better) 0MB 375MB 750MB 1125MB

    1500MB Memory usage for 8 workers vs 8 threads 800MB 1,430MB 1,100MB CRuby JRuby (default) JRuby (300MB heap)
  51. Monitoring and Profiling

  52. JVM Tooling • Visual VM: monitor and profile threads, GC,

    managed heap • Mission Control: visualize Flight Recorder output • Flight Recorder: low-overhead system profiling in OpenJDK • async-profiler: command line profiles, flame graphs of CPU, alloc • All JVM tools work to monitor and profile JRuby apps
  53. VisualVM • Standalone graphical console for monitoring, profiling • Open

    sourced as part of OpenJDK project •
  54. Visual VM and Visual GC

  55. JDK Flight Recorder • JVM flag: -XX:+FlightRecorder • JRuby flag:

    -J-XX:+FlightRecorder or put in JAVA_OPTS • Add --flight-recorder to JRuby launcher? • No overhead or profiling until you start recording • And usually less than 1% overhead for that
  56. JDK Mission Control • Control center and visualizer for Flight

    Recorder data • Start and manage recordings • Browse recorded data •
  57. None
  58. None
  59. async-profiler • JVM extension for lightweight sampled profiles • Install

    jruby-async-profiler gem • jruby -J-agentpath:<jruby home>/lib/ • Simplified command lines coming soon • See
  60. None
  61. Final Words

  62. During Development • JRUBY_OPTS=--dev • BE WARY of .ruby-version •

    Don’t share gem paths between Ruby implementations • Java and C extensions clobber each other
  63. Java 9+ Module System • New restrictions on access to

    core Java classes • You'll see warnings about JRuby or Ruby accesses • Config file for JVM flags: .jruby.java_opts • All your JVM options into a single file • Current dir, home dir, JRuby bin/ dir
  64. Java Modules and .jruby.java_opts WARNING: An illegal reflective access operation

    has occurred WARNING: Illegal reflective access by org.bouncycastle.jcajce.provider.drbg.DRBG (file:/Users/headius/.m2/repository/org/ bouncycastle/bcprov-jdk15on/1.61/bcprov-jdk15on-1.61.jar) to constructor WARNING: Please consider reporting this to the maintainers of org.bouncycastle.jcajce.provider.drbg.DRBG WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release --add-opens java.base/ --add-opens java.base/java.nio.channels=org.jruby.dist --add-opens java.base/ --add-opens
  65. Getting Help • JRuby on GitHub: • Issues, Wiki

    • Chat with JRuby devs, users • jruby on Matrix • #jruby on Freenode IRC • Mailing list:
  66. JRuby Workshop • Free-form "getting started" plus hackfest • Get

    JRuby installed and try it out • Test out your libraries • Try to get your apps running
  67. Thank You! • @headius, @tom_enebo • • •

    Chat with JRuby devs, users • #jruby on Freenode IRC • jruby on Matrix • Mailing list: