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
Use a String to Access a Local Variable by Name
Rails + Mongoid - Don't Return Nil Values in JSON
Strong Parameters with Nested Hash
Difference Between "Class A; Class B" and "Class A::B"
How to Handle 404 Errors with Ruby Http::Net
How to Read Text from Non Visible Elements with Watir (Ruby)
Ruby - Append Content at The End of The Existing S3 File Using Fog
Paperclip and Amazon S3 How to Do Paths
Deleting Items from an Array Requires Multiple Passes to Remove Them All
Permission Error When Trying to Install Rails (Osx)
What Is Toplevel_Binding in Ruby
Overriding Instance Variable Array's Operators in Ruby
Ruby on Rails Private Link Sharing: Google Docs Style
Ubuntu 10 Ruby 1.9 Rails 3: No Such File or Directory
Camelcase Instead of Snake_Case in Rails Db
How to Resize Image Only If Width Exceeds with Graphics/Image Magick