Ruby & JVM:
A (Jruby) Love Story
Yarden Laifenfeld
@yardenlaif
Slide 2
Slide 2 text
2
Who Am I?
• Software Engineer at Rookout
• Background in low level C
programming in linux IOT
environments
• Ruby, Java, Go
• C#, Python, JavaScript, C++
Slide 3
Slide 3 text
What is JRuby?
3
“ JRuby is a 100% Java implementation of
the Ruby programming language. It is Ruby
for the JVM. ”
- the JRuby wiki
Slide 4
Slide 4 text
4
Should you use JRuby?
Slide 5
Slide 5 text
JRuby Advantages
5
● Integration with Java libraries
● Running atop the JVM
○ Threads
○ Long running applications
Slide 6
Slide 6 text
Why JRuby.md - Jordan Sissel, 2010
https://gist.github.com/jordansissel/978956
6
● core and stdlib ruby changes violently and without notice and
without backwards compatibility. I want nothing of that.
● need a cross-ruby date library that isn't part of stdlib (see
previous point) and is also good.
● need an easy way to use multiple cpus that is cross-ruby
(threads are not it)
Slide 7
Slide 7 text
CRuby Advantages
7
● Larger community
● More features
Slide 8
Slide 8 text
8
Should you use JRuby?
I don’t know, you choose.
Slide 9
Slide 9 text
Supporting JRuby in one of
Rookout’s debuggers
9
Slide 10
Slide 10 text
Supporting JRuby
10
❏ Running an app
❏ Adding a breakpoint
❏ Collecting data
Slide 11
Slide 11 text
Decision Time
11
Java or Ruby
Slide 12
Slide 12 text
Decision Time
12
Java or Ruby
Slide 13
Slide 13 text
Running an app
13
● rails server -p 8080 -e development
Slide 14
Slide 14 text
Running an app
14
rvm install jruby
rvm use jruby
bundle install
Slide 15
Slide 15 text
Running an app
15
Slide 16
Slide 16 text
Running an app
16
3 business days
later…
Slide 17
Slide 17 text
Running an app
17
bundle config unset force_ruby_platform
Slide 18
Slide 18 text
Running an app
18
➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development
=> Booting Puma
=> Rails 6.0.3.4 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.7 (jruby 9.2.9.0 - ruby 2.5.7), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://[::1]:8080
* Listening on tcp://127.0.0.1:8080
Use Ctrl-C to stop
Slide 19
Slide 19 text
Running an app
19
➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development
=> Booting Puma
=> Rails 6.0.3.4 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.7 (jruby 9.2.9.0 - ruby 2.5.7), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://[::1]:8080
* Listening on tcp://127.0.0.1:8080
Use Ctrl-C to stop
Slide 20
Slide 20 text
Running an app
20
➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development
=> Booting Puma
=> Rails 6.0.3.4 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.7 (jruby 9.2.9.0 - ruby 2.5.7), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://[::1]:8080
* Listening on tcp://127.0.0.1:8080
Use Ctrl-C to stop
Slide 21
Slide 21 text
Supporting JRuby
21
? Running an app
❏ Adding a breakpoint
❏ Collecting data
Running an app
23
➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e
development
Slide 24
Slide 24 text
Running an app
24
➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e
development
[Rookout] Successfully connected to controller.
Slide 25
Slide 25 text
Running an app
25
➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development
[Rookout] Successfully connected to controller.
=> Booting Puma
=> Rails 6.0.6 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.12 (jruby 9.3.8.0 - ruby 2.6.8), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://[::1]:8080
* Listening on tcp://127.0.0.1:8080
Use Ctrl-C to stop
Slide 26
Slide 26 text
Supporting JRuby
26
✓ Running an app
❏ Adding a breakpoint
❏ Collecting data
Slide 27
Slide 27 text
TracePoints
27
Slide 28
Slide 28 text
TracePoints
28
tracepoint = Tracepoint.new(:line) do
...
end
Slide 29
Slide 29 text
TracePoints
29
tracepoint = Tracepoint.new(:line) do
...
end
tracepoint.enable
Slide 30
Slide 30 text
TracePoints
30
tracepoint = Tracepoint.new(:line) do
...
end
tracepoint.enable(target: method(:m1),
target_line: 14)
Slide 31
Slide 31 text
TracePoints in JRuby
31
Slide 32
Slide 32 text
Workaround
32
tracepoint.enable
Slide 33
Slide 33 text
Supporting JRuby
33
✓ Running an app
✗ Adding a breakpoint
❏ Collecting data
Slide 34
Slide 34 text
Decision Time
34
Java or Ruby
Slide 35
Slide 35 text
Decision Time
35
Java or Ruby
Slide 36
Slide 36 text
Supporting JRuby
36
❏ Running an app
❏ Adding a breakpoint
❏ Collecting data
Slide 37
Slide 37 text
Running an app - Java Agents
37
● -javaagent flag
● premain entrypoint
● Instrumentation API
App
JavaAgent
Instrumentation API
Slide 38
Slide 38 text
Running an app
38
➜ tutorial-ruby git:(master) ✗ jruby -J-javaagent:rook.jar -S rails
server -p 8080 -e development
Slide 39
Slide 39 text
Running an app
39
➜ tutorial-ruby git:(master) ✗ jruby -J-javaagent:rook.jar -S rails
server -p 8080 -e development
[Rookout] Successfully connected to controller.
Slide 40
Slide 40 text
Running an app
40
➜ tutorial-ruby git:(master) ✗ jruby -J-javaagent:rook.jar -S rails server -p 8080 -e
development
[Rookout] Successfully connected to controller.
=> Booting Puma
=> Rails 6.0.6 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.12 (jruby 9.3.8.0 - ruby 2.6.8), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://[::1]:8080
* Listening on tcp://127.0.0.1:8080
Use Ctrl-C to stop
Slide 41
Slide 41 text
Supporting JRuby
41
✓ Running an app
❏ Adding a breakpoint
❏ Collecting data
Slide 42
Slide 42 text
Adding a breakpoint - Class Loaders
42
JVM
Compiler
Code Bytecode
Slide 43
Slide 43 text
Adding a breakpoint - Class Loaders
43
JVM
32 | …
33 | new Item()
34 | …
Class
Loader
Get Item
class bytecode
Return Item
class bytecode
Slide 44
Slide 44 text
Adding a breakpoint - Class Loaders
44
JVM
32 | …
33 | new Item()
34 | …
Class Loader
Get Item class
bytecode
Return Item
class bytecode
Class
Transformer
Return
transformed Item
class bytecode
Slide 45
Slide 45 text
Adding a breakpoint
45
Expectation: Works straight away!
Reality: Doesn’t work at all.
Slide 46
Slide 46 text
46
Slide 47
Slide 47 text
47
Slide 48
Slide 48 text
48
Slide 49
Slide 49 text
49
Slide 50
Slide 50 text
50
Slide 51
Slide 51 text
51
Slide 52
Slide 52 text
52
Slide 53
Slide 53 text
53
A few, spaced out invocations
Many, rapid invocations
Class isn’t loaded
Class is loaded
Slide 54
Slide 54 text
54
A few, spaced out invocations
Many, rapid invocations
Class isn’t loaded
Class is loaded
JIT
Supporting JRuby
60
✓ Running an app
✓ Adding a breakpoint
❏ Collecting data
Slide 61
Slide 61 text
Collecting data
61
class TasksController
def create
params.require(:task).permit(:task)
task = Task.new(params[:task][:title])
$tasks_storage.push(task)
redirect_back(fallback_location: 'tasks')
end
end
Slide 62
Slide 62 text
Collecting data
62
class TasksController
def create
params.require(:task).permit(:task)
task = Task.new(params[:task][:title])
$tasks_storage.push(task)
redirect_back(fallback_location: 'tasks')
end
end
Slide 63
Slide 63 text
Collecting data
63
class TasksController
def create
params.require(:task).permit(:task)
task = Task.new(params[:task][:title])
$tasks_storage.push(task)
redirect_back(fallback_location: 'tasks')
end
end
Slide 64
Slide 64 text
Collecting data
64
Slide 65
Slide 65 text
Collecting data - self
65
Slide 66
Slide 66 text
Collecting data - locals
66
Slide 67
Slide 67 text
Supporting JRuby
67
✓ Running an app
✓ Adding a breakpoint
✓ Collecting data
Slide 68
Slide 68 text
What Next?
68
● Ruby TracePoint API
● Java Agents
● Class Loaders
@yardenlaif