Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Getting JRuby in 3 Steps

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Step 2: Install JRuby

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Step 3: Profit!

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Native Threads vs Global Interpreter Lock

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Ruby 2.5.7 unthreaded JRuby unthreaded Ruby 2.5.7 threaded JRuby threaded

Slide 14

Slide 14 text

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)

Slide 15

Slide 15 text

Virtual Machine vs Going It Alone

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Java bytecode == portability

Slide 25

Slide 25 text

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!

Slide 26

Slide 26 text

Ruby C Extensions vs Java Native Extensions

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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/

Slide 33

Slide 33 text

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/

Slide 34

Slide 34 text

Scripting Java

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

JRuby IRB

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

JRuby Performance

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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)

Slide 55

Slide 55 text

Migrating An App

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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!

Slide 58

Slide 58 text

Step 1: jruby-lint

Slide 59

Slide 59 text

jruby-lint gem See how ready your Ruby code is to run on JRuby $ gem install jruby-lint $ cd my-app $ jrlint

Slide 60

Slide 60 text

[] ~/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

Slide 61

Slide 61 text

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'

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Step 2: Replace C Extensions

Slide 66

Slide 66 text

% 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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

Monitoring and Profiling

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

Visual VM and Visual GC

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

Final Words

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

Try JRuby!

Slide 90

Slide 90 text

Let us know if you have any questions!

Slide 91

Slide 91 text

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