Loading/Unloading/Updating Class in Ruby

Force Class Loading and Unloading (RoR)

Since you tagged the question with Rails, then you can do

some_class_name.constantize

Example

"User".constantize
# => User

remove_const works, but you need to call it from the object space where the constant is defined. Also, remember to pass a symbol as the constant name.

Example

Object.send(:remove_const, :Foo)

Can I Unload a Rails Metal Class after one time use?

A simplest solution (although not quite as clean) would be to store the fact that an admin user exists in the class.

class EnsureAdminUser
def self.call(env)
if @admin_defined or Admin.any?
@admin_defined = true
[404, {"Content-Type" => "text/html"}, "Not Found"]
else

end
end
end

This saves you the DB hit on each request.

To actually delete the metal you will need to do something a bit more radical (and evil):

ObjectSpace.each_object(Rails::Rack::Metal){|metal_handler|
metal_handler.instance_eval{ @metals.delete(EnsureAdminUser) }
}

Module loading issue in Ruby

generatorFunction is a class method. It doesn't see instance-level methods. And myhelper (brought in by include Helpers) is an instance method. To remedy that, you should extend Helpers instead. It works like include, but makes class methods.

class FileGenerator
extend Helpers

end

BTW, the name generatorFunction is not in ruby style. You should name methods in snake_case (generator_function).

Loading JRuby at runtime and ClassLoader leak

Edit: reported this as a bug: JRUBY-6522, now fixed.

After digging around in the Eclipse Memory Analyzer, I clicked "path to GC" on one of the URLClassLoader instances. It was referenced by org.jruby.RubyEncoding$2 which was referenced by java.lang.ThreadLocal$ThreadLocalMap$Entry.

Looking inside that source file, I see a static ThreadLocal variable being created: RubyEncoding.java:266. ThreadLocals are presumably hanging around forever, referencing my ClassLoader and leaking memory.

This code example succeeds:

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Test;

public class JRubyInstantiationTeardownTest {

public static int i;

@Test
public void test() throws Exception {

for (i = 0; i < 100; ++i) {

URL[] urls = new URL[] {
new URL("file:///home/pat/jruby-1.6.7/lib/jruby.jar")
};

final ClassLoader cl = new URLClassLoader(urls, this.getClass().getClassLoader());

final Class<?> rubyClass = cl.loadClass("org.jruby.Ruby");
final Method newInstance = rubyClass.getMethod("newInstance");
final Method evalScriptlet = rubyClass.getMethod("evalScriptlet", String.class);
final Method tearDown = rubyClass.getMethod("tearDown");

// "Direct" API
Callable<Void> direct = new Callable<Void>() {
public Void call() throws Exception {
// created inside thread because initialization happens immediately
final Object ruby = newInstance.invoke(null);

System.out.println("" + i + ": " + ruby);
evalScriptlet.invoke(ruby, "puts 'hello, world'");
tearDown.invoke(ruby);
return null;
}
};

// JRuby Embed API
final Class<?> scriptingContainerClass = cl.loadClass("org.jruby.embed.ScriptingContainer");
final Method terminate = scriptingContainerClass.getMethod("terminate");
final Method runScriptlet = scriptingContainerClass.getMethod("runScriptlet", String.class);

// created outside thread because ruby instance not created immediately
final Object container = scriptingContainerClass.newInstance();

Callable<Void> embed = new Callable<Void>() {
public Void call() throws Exception {

System.out.println(i + ": " + container);
runScriptlet.invoke(container, "puts 'hello, world'");
terminate.invoke(container);
return null;
}
};

// separate thread for each loop iteration so its ThreadLocal vars are discarded
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(direct).get();
executor.submit(embed).get();
executor.shutdown();
}
}

}

Now I'm wondering if this is acceptable behavior of JRuby, or what JRuby-Rack does in the context of a servlet container where the servlet container is managing its own thread pool to process requests. It seems like one would need to maintain a completely separate thread pool, only execute Ruby code in those threads, and then ensure they get destroyed when the servlet is undeployed...

This is very relevant: Tomcat Memory Leak Protection

See also JVM bug report: Provide reclaimable thread local values without Thread termination

Updating multiple hash keys after merging values

You can't switch out one object for another unless you have some kind of a wrapper. Unless performance matters a lot, the easiest wrappers to use are proxy objects, because you don't need to unwrap them: they transparently behave exactly like the wrapped object.

class ProxyObject
# thanks to https://alisdair.mcdiarmid.org/invisible-proxies-with-ruby/
instance_methods.each do |m|
undef_method(m) unless m =~ /(^__|^nil\?$|^send$|^object_id$)/
end

attr_accessor :target

def initialize(target)
@target = target
end

def respond_to?(symbol, include_priv=false)
@target.respond_to?(symbol, include_priv)
end

private def method_missing(method, *args, &block)
@target.send(method, *args, &block)
end
end

a = 1
b = 10
a_proxy = ProxyObject.new(a)
b_proxy = ProxyObject.new(b)
a_proxy.class # verify how well they masquerade
# => Integer
hash = 10.times.map { |i| [i + 1, i < 5 ? a_proxy : b_proxy] }.to_h
# => {1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>10, 7=>10, 8=>10, 9=>10, 10=>10}
hash.values.sum() # confirm it behaves exactly like a number
# => 55
b_proxy.target = a_proxy.target # switch reference
hash
# => {1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1, 10=>1}
hash.values.sum() # confirm the reference is changed
# => 10


Related Topics



Leave a reply



Submit