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

JRuby: Zero to Scale

headius
November 20, 2019

JRuby: Zero to Scale

JRuby is deployed by hundreds of companies, running Rails and other services at higher speeds and with better scalability than any other runtime. With JRuby you get better utilization of system resources, the performance and tooling of the JVM, and a massive collection of libraries to add to your toolbox.

In this talk, we'll cover:

* Getting started on JRuby
* Comparison to CRuby
* Building, migrating, and deploying apps
* Tuning, profiling, and monitoring
* Scaling considerations

headius

November 20, 2019
Tweet

More Decks by headius

Other Decks in Programming

Transcript

  1. JRuby: Zero to Scale
    Thomas E. Enebo (@tom_enebo)
    Charles O. Nutter (@headius)

    View Slide

  2. • JRuby co-leads
    • Red Hat
    Charles Thomas Ruby
    Java
    Beer

    View Slide

  3. What is JRuby?
    Ruby Implementation (2.5.7 compatible)
    …which happens to run on the
    Java Virtual Machine (JVM)

    View Slide

  4. JRuby 9.2.9.0
    • Lots of memory and startup time 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

    View Slide

  5. Getting JRuby in 3 Steps

    View Slide

  6. Step 1: You Need a JVM
    • Java (version 8 or 11 recommended)
    • Is it installed? java -version
    • If !installed
    • package install: dnf/apt install java
    • explicit install: http://adoptopenjdk.net

    View Slide

  7. Step 2: Install JRuby

    View Slide

  8. Step 2: Install JRuby on Windows
    www.jruby.org

    View Slide

  9. Step 3: Profit!

    View Slide

  10. Why JRuby?
    What are the differences?
    - JRuby - C Ruby

    View Slide

  11. Native Threads
    vs
    Global Interpreter Lock

    View Slide

  12. 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 {
    Thread.new {
    ary.each {|i|}
    }
    }.map(&:join)
    }
    }
    Multi-threaded

    View Slide

  13. Ruby 2.5.7 unthreaded
    JRuby unthreaded
    Ruby 2.5.7 threaded
    JRuby threaded

    View Slide

  14. 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)

    View Slide

  15. Virtual Machine
    vs
    Going It Alone

    View Slide

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

    View Slide

  17. 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

    View Slide

  18. 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

    View Slide

  19. !Perspective!
    ???
    Total control can be great!

    View Slide

  20. 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

    View Slide

  21. 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

    View Slide

  22. 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

    View Slide

  23. JVM is everywhere++!
    • Unix++, Windows
    • Exotic platforms: zLinux, OpenVMS, AS/400
    • Mobile: Android’s Dalvik, Embedded JVMs

    View Slide

  24. Java bytecode
    ==
    portability

    View Slide

  25. 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!

    View Slide

  26. Ruby C Extensions
    vs
    Java Native Extensions

    View Slide

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

    View Slide

  28. C Extensions
    • API is massive
    • API is specific to C Ruby’s implementation
    • It is the implementation
    • At odds with concurrent Ruby execution
    • Huge support cost

    View Slide

  29. 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

    View Slide

  30. oj: Optimized json
    • Fast json parsing and dumping with many options
    • Common transitive dependency with its own API
    • Needed for many popular apps/libraries
    https://github.com/ohler55/oj

    View Slide

  31. oj for JRuby!
    • 9200 lines of Java (vs 20k lines of C)
    • Almost ready: 448 runs, 765 assertions, 43 failures, 12 errors
    • 35 F/E from minor features, date/time diffs

    View Slide

  32. 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)
    https://techblog.thescore.com/2014/05/23/benchmarking-json-generation-in-ruby/

    View Slide

  33. 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)
    https://techblog.thescore.com/2014/05/23/benchmarking-json-generation-in-ruby/

    View Slide

  34. Scripting Java

    View Slide

  35. Scripting Java
    • Access Java Classes using a Ruby Syntax
    • Java Libraries
    • Give alternatives to existing Ruby Libraries
    • Give access to APIs which do not exist in Ruby

    View Slide

  36. JRuby IRB

    View Slide

  37. Fun Stuff
    event(:player_egg_throw) do |e|
    e.hatching = true
    e.num_hatches = 120
    e.player.mesg "hatched"
    end
    Purugin
    https://github.com/enebo/Purugin

    View Slide

  38. JRuby Performance

    View Slide

  39. 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

    View Slide

  40. 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

    View Slide

  41. 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.15s
    2.3s
    3.45s
    4.6s
    gem list (~350 gems)
    3.0s
    4.6s
    JRuby JRuby --dev

    View Slide

  42. 0
    1
    2
    3
    4
    gem list
    3.05
    0.939
    CRuby JRuby --dev

    View Slide

  43. 0
    1.75
    3.5
    5.25
    7
    rails console
    6.7
    4.96
    CRuby JRuby --dev

    View Slide

  44. OpenJDK Class Data Sharing
    • Pre-validate and cache class data
    • Save frequently-accessed data over time
    • Combined with --dev gives best current JRuby startup

    View Slide

  45. Java 11
    total execution time (lower is better)
    0
    1.75
    3.5
    5.25
    7
    gem list (~350 gems)
    JRuby --dev --dev + CDS

    View Slide

  46. App Performance
    • Sinatra and Roda
    • https://github.com/CaDs/ruby_benchmarks
    • Comparing JRuby, CRuby, and TruffleRuby
    • RedMine bug tracker and wiki
    • https://www.redmine.org/
    • Real-world Rails application on JRuby, CRuby

    View Slide

  47. Peak Performance
    • After initial startup, warmup, data caching
    • JRuby generally gives better peak performance
    • Be aware of warmup time

    View Slide

  48. Sinatra and Roda requests/second (higher is better)
    0
    12500
    25000
    37500
    50000
    Sinatra Roda
    5,214
    4,257
    44,703
    42,468
    14,489
    12,284
    CRuby JRuby TruffleRuby

    View Slide

  49. 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

    View Slide

  50. Warmup Time
    • Large applications take longer to warm up
    • Working on new JIT metrics, profiling to reduce this curve

    View Slide

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

    View Slide

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

    View Slide

  53. 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

    View Slide

  54. 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)

    View Slide

  55. Migrating An App

    View Slide

  56. Migration Process
    • Existing application Activities:
    • Configuration Changes (e.g. config/database.yml)
    • C extensions replacement
    • Thread-safety audit

    View Slide

  57. Use-Case: Discourse
    • “A platform for community discussion”
    • Very large, well-known Rails application
    • >500 gems
    • 250,000 lines of Ruby
    • JRuby is not currently supported
    • But it is almost working!

    View Slide

  58. Step 1: jruby-lint

    View Slide

  59. jruby-lint gem
    See how ready your Ruby code is to run on JRuby
    $ gem install jruby-lint

    $ cd my-app

    $ jrlint

    View Slide

  60. [] ~/projects/discourse $ jrlint
    JRuby-Lint version 0.9.0
    ./Gemfile:: [gems, info] For more on gem compatibility see http://wiki.jruby.org/C-Extension-Alternatives
    ./Gemfile:80: [gems, warning] Found gem 'oj' which is reported to have some issues:
    Try `gson`, `json` or `json_pure` instead.|
    gem 'oj'
    ./Gemfile:81: [gems, warning] Found gem 'pg' which is reported to have some issues:
    Use activerecord-jdbcpostgresql-adapter instead or pg_jruby (drop-in replacement).|
    gem 'pg'
    ./Gemfile:187: [gems, warning] Found gem 'mysql2' which is reported to have some issues:
    Use activerecord-jdbcmysql-adapter.|
    gem 'mysql2'
    ./Gemfile:188: [gems, warning] Found gem 'redcarpet' which is reported to have some issues:
    Same as with **RDiscount** use alternatives such as kramdown, Maruku or markdown_j|
    gem 'redcarpet'
    ./Gemfile:189: [gems, warning] Found gem 'sqlite3' which is reported to have some issues:
    Use activerecord-jdbcsqlite3-adapter.|
    gem 'sqlite3', '~> 1.3.13'
    ./app/mailers/user_notifications.rb:152: [nonatomic, warning] Non-local operator assignment (@popular_topics) is not guaranteed to be atomic.
    @popular_topics = topics_for_digest[0, SiteSetting.digest_topics]
    ./app/mailers/user_notifications.rb:630: [nonatomic, warning] Non-local operator assignment (@site_name) is not guaranteed to be atomic.
    @site_name = SiteSetting.email_prefix.presence || SiteSetting.title # used by I18n
    ./app/models/report.rb:21: [nonatomic, warning] Non-local operator assignment (@start_date) is not guaranteed to be atomic.
    @start_date ||= Report.default_days.days.ago.utc.beginning_of_day
    ./app/models/report.rb:22: [nonatomic, warning] Non-local operator assignment (@end_date) is not guaranteed to be atomic.
    @end_date ||= Time.now.utc.end_of_day
    ./app/models/admin_dashboard_next_data.rb:17: [nonatomic, warning] Non-local operator assignment (@json) is not guaranteed to be atomic.
    @json ||= get_json
    ./app/models/topic_posters_summary.rb:31: [nonatomic, warning] Non-local operator assignment (@descriptions_by_id) is not guaranteed to be atomic.
    @descriptions_by_id ||= begin
    ./app/models/topic_posters_summary.rb:33: [nonatomic, warning] Non-local operator assignment (descriptions[id]) is not guaranteed to be atomic.
    descriptions[id] ||= []
    ./app/models/topic_posters_summary.rb:79: [nonatomic, warning] Non-local operator assignment (@avatar_lookup) is not guaranteed to be atomic.
    @avatar_lookup ||= options[:avatar_lookup] || AvatarLookup.new(user_ids)
    ./app/models/topic_posters_summary.rb:83: [nonatomic, warning] Non-local operator assignment (@primary_group_lookup) is not guaranteed to be atomic.
    @primary_group_lookup ||= options[:primary_group_lookup] || PrimaryGroupLookup.new(user_ids)
    ./app/models/directory_item.rb:8: [nonatomic, warning] Non-local operator assignment (@headings) is not guaranteed to be atomic.
    @headings ||= [:likes_received,
    ./app/models/directory_item.rb:18: [nonatomic, warning] Non-local operator assignment (@types) is not guaranteed to be atomic.
    @types ||= Enum.new(all: 1,
    ./app/models/group_history.rb:11: [nonatomic, warning] Non-local operator assignment (@actions) is not guaranteed to be atomic.
    @actions ||= Enum.new(
    ./app/models/locale_site_setting.rb:10: [nonatomic, warning] Non-local operator assignment (@values) is not guaranteed to be atomic.
    @values ||= supported_locales.map do |locale|
    ./app/models/locale_site_setting.rb:25: [nonatomic, warning] Non-local operator assignment (@language_names) is not guaranteed to be atomic.
    @language_names ||= begin
    ./app/models/locale_site_setting.rb:41: [nonatomic, warning] Non-local operator assignment (@supported_locales) is not guaranteed to be atomic.
    @supported_locales ||= begin
    ./app/models/category.rb:98: [nonatomic, warning] Non-local operator assignment (TOPIC_CREATION_PERMISSIONS) is not guaranteed to be atomic.
    TOPIC_CREATION_PERMISSIONS ||= [:full]
    ./app/models/category.rb:99: [nonatomic, warning] Non-local operator assignment (POST_CREATION_PERMISSIONS) is not guaranteed to be atomic.
    POST_CREATION_PERMISSIONS ||= [:create_post, :full]
    ./app/models/category.rb:109: [nonatomic, warning] Non-local operator assignment (@topic_id_cache) is not guaranteed to be atomic.
    @topic_id_cache = DistributedCache.new('category_topic_ids')
    ./app/models/category.rb:228: [nonatomic, warning] Non-local operator assignment (@@cache) is not guaranteed to be atomic.
    @@cache ||= LruRedux::ThreadSafeCache.new(1000)
    ./app/models/category.rb:520: [nonatomic, warning] Non-local operator assignment (@has_children) is not guaranteed to be atomic.
    @has_children ||= (id && Category.where(parent_category_id: id).exists?) ? :true : :false

    View Slide

  61. Unsupported Extensions
    ./Gemfile:: [gems, info] For more on gem compatibility see http://wiki.jruby.org/C-Extension-Alternatives
    ./Gemfile:80: [gems, warning] Found gem 'oj' which is reported to have some issues:
    Try `gson`, `json` or `json_pure` instead.|
    gem 'oj'
    ./Gemfile:81: [gems, warning] Found gem 'pg' which is reported to have some issues:
    Use activerecord-jdbcpostgresql-adapter instead or pg_jruby (drop-in replacement).|
    gem 'pg'
    ./Gemfile:187: [gems, warning] Found gem 'mysql2' which is reported to have some issues:
    Use activerecord-jdbcmysql-adapter.|
    gem 'mysql2'
    ./Gemfile:188: [gems, warning] Found gem 'redcarpet' which is reported to have some issues:
    Same as with **RDiscount** use alternatives such as kramdown, Maruku or markdown_j|
    gem 'redcarpet'
    ./Gemfile:189: [gems, warning] Found gem 'sqlite3' which is reported to have some issues:
    Use activerecord-jdbcsqlite3-adapter.|
    gem 'sqlite3', '~> 1.3.13'

    View Slide

  62. View Slide

  63. Threading Concerns
    ./app/models/report.rb:21: [nonatomic, warning] Non-local operator assignment (@start_date) is
    not guaranteed to be atomic.
    @start_date ||= Report.default_days.days.ago.utc.beginning_of_day
    ./app/models/report.rb:22: [nonatomic, warning] Non-local operator assignment (@end_date) is not
    guaranteed to be atomic.
    @end_date ||= Time.now.utc.end_of_day
    ./app/models/admin_dashboard_next_data.rb:17: [nonatomic, warning] Non-local operator assignment
    (@json) is not guaranteed to be atomic.
    @json ||= get_json
    ./app/models/topic_posters_summary.rb:31: [nonatomic, warning] Non-local operator assignment
    (@descriptions_by_id) is not guaranteed to be atomic.
    @descriptions_by_id ||= begin
    ./app/models/topic_posters_summary.rb:33: [nonatomic, warning] Non-local operator assignment
    (descriptions[id]) is not guaranteed to be atomic.
    descriptions[id] ||= []
    ./app/models/topic_posters_summary.rb:79: [nonatomic, warning] Non-local operator assignment
    (@avatar_lookup) is not guaranteed to be atomic.
    @avatar_lookup ||= options[:avatar_lookup] || AvatarLookup.new(user_ids)
    ./app/models/topic_posters_summary.rb:83: [nonatomic, warning] Non-local operator assignment
    (@primary_group_lookup) is not guaranteed to be atomic.
    @primary_group_lookup ||= options[:primary_group_lookup] || PrimaryGroupLookup.new(user_ids)
    @language_names) is not guaranteed to be atomic.
    @language_names ||= begin

    View Slide

  64. Unsupported Features
    ./script/measure.rb:48: [objectspace, warning]
    Use of ObjectSpace is expensive and disabled
    by default. Use -X+O to enable.
    ObjectSpace.each_object do |o|
    ./script/check_forking.rb:14: [fork, error]
    Kernel#fork is not implemented on JRuby.
    child = fork do
    ./script/check_forking.rb:17: [fork, error]
    Kernel#fork is not implemented on JRuby.
    grand_child = fork do

    View Slide

  65. Step 2: Replace C Extensions

    View Slide

  66. % bundle install
    Fetching pg 1.1.4
    Installing pg 1.1.4 with native extensions
    Gem::Ext::BuildError: ERROR: Failed to build gem native extension
    gem 'pg'
    Gemfile Gemfile
    gem 'pg', platform: :mri
    gem 'activerecord-jdbcpostgresql-adapter', platform: :jruby

    View Slide

  67. Missing C Extension
    1.Remove it if not needed (development dep like byebug)
    2.Use a pure-Ruby version if performance is good enough
    3.Call into a native library using FFI (Foreign Function Interface)
    4.“Script” a Java library
    5.Write a JRuby extension

    View Slide

  68. Step 3: Run It
    • Once your application bundles, it should run!
    • If not talk to us or file an issue
    puma -t 20:20 -e production

    View Slide

  69. Monitoring and Profiling

    View Slide

  70. 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

    View Slide

  71. VisualVM
    • Standalone graphical console for monitoring, profiling
    • Open sourced as part of OpenJDK project
    • https://visualvm.github.io/

    View Slide

  72. View Slide

  73. Visual VM and Visual GC

    View Slide

  74. 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

    View Slide

  75. JDK Mission Control
    • Control center and visualizer for Flight Recorder data
    • Start and manage recordings
    • Browse recorded data
    • https://adoptopenjdk.net/jmc.html

    View Slide

  76. View Slide

  77. View Slide

  78. View Slide

  79. View Slide

  80. View Slide

  81. View Slide

  82. async-profiler
    • JVM extension for lightweight sampled profiles
    • Install jruby-async-profiler gem
    • jruby -J-agentpath:/lib/libasyncProfiler.so
    • Simplified command lines coming soon
    • See https://github.com/jvm-profiling-tools/async-profiler

    View Slide

  83. View Slide

  84. Final Words

    View Slide

  85. Getting Help
    • JRuby on GitHub: https://github.com/jruby/jruby
    • Issues, Wiki
    • Chat with JRuby devs, users
    • jruby on Matrix
    • #jruby on Freenode IRC
    • Mailing list: https://lists.ruby-lang.org

    View Slide

  86. 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

    View Slide

  87. Java Modules
    • Java 9+ introduced Java Modules
    • You'll see warnings about JRuby or Ruby accesses
    • New config file for JVM flags: .jruby.java_opts
    • Gather JVM options into a single file
    • Current dir, home dir, JRuby bin/ dir

    View Slide

  88. 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 sun.security.provider.Sun()
    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/java.io=org.jruby.dist
    --add-opens java.base/java.nio.channels=org.jruby.dist
    --add-opens java.base/sun.nio.ch=org.jruby.dist
    --add-opens java.management/sun.management=org.jruby.dist

    View Slide

  89. Try JRuby!

    View Slide

  90. Let us know if you have any
    questions!

    View Slide

  91. Thank You!
    • @headius, @tom_enebo
    • https://www.jruby.org
    • https://github.com/jruby/jruby
    • Chat with JRuby devs, users
    • #jruby on Freenode IRC
    • jruby on Matrix
    • Mailing list: https://lists.ruby-lang.org

    View Slide