Clueless? Throwable to the rescue



  • I removed 22 instances of "catch Throwable" from our code base the other day. Here's one of them:

    // Converter for date
    private static String formatDate(Date date,
    		SimpleDateFormat simpledateformat) {
    	try {
    		return simpledateformat.format(date);
    	} catch (Throwable _ex) {
    		return "";
    	}
    }

    For those of you who don't know, Throwable is the root exception in Java, and thus includes system and JVM errors such as running out of memory. I replaced Throwable with IllegalArgumentException, which is the only exception expected to be thrown by the formatting function.

    Some Throwables were harder to get rid of:

    public void callFunction(...) throws Throwable
    try {
    	...
    	method.invoke(this, params);
    	...
    } catch (InvocationTargetException e) {
    	throw e.getTargetException();
    }

    To be fair, TRWTF here is that "getTargetException()" actually returns a Throwable, and the documentation doesn't explicitly state whether Errors get trapped or not.

    While I was at it, I also removed some empty catch blocks. Here's one of those:

    String[ ] PREFIX = {"get", "is"}
    for (String s : PREFIX) {
        try {
            return type.getMethod(s + methodName);
        } catch (NoSuchMethodException e) {
            // ignore
        }
    }
    return null;

     



  • @Faxmachinen said:

    Throwable is the root exception in Java, and thus includes system and JVM errors such as running out of memory.

    <pedantic dickweed mode> It's actually the superclass for both Exceptions and Errors, not all Throwable instances are Exceptions (as you yourself pointed out, Errors are Throwables too) </pedantic dickweed mode>


  • @dtech said:

    @Faxmachinen said:
    Throwable is the root exception in Java, and thus includes system and JVM errors such as running out of memory.

    It's actually the superclass for both Exceptions and Errors, not all Throwable instances are Exceptions (as you yourself pointed out, Errors are Throwables too)

    Of course he didn't say "root Exception", he said "root exception", which I took as a reference to the generic programming concept of exceptions (of which Throwable is definitely a part.) If someone said "In Java the keyword 'throw' is used to throw exceptions" would you butt in with "Yeah, but you can also throw subclasses of Error!"? It's really just dickweedishly pedantic and--as cases of pedantry often are--arguably incorrect.



  •  TRWTF is the Java compiler insisting that any method throwing a Throwable must use "throws Throwable" even through Throwable shouldn’t be caught by the caller.

    "throws Exception" should suffice in that case. After all, a Throwable which isn’t an Exception is an Error, and any method call can throw an Error.



  • @Faxmachinen said:

    While I was at it, I also removed some empty catch blocks. Here's one of those:

    String[ ] PREFIX = {"get", "is"}
    for (String s : PREFIX) {
        try {
            return type.getMethod(s + methodName);
        } catch (NoSuchMethodException e) {
            // ignore
        }
    }
    return null;

     

    This code looks right the way it is. "Iterate over possible method names, return if the method exists; if none was found return null". If you remove the catch, the loop can never continue.

     



  • @VinDuv said:

     TRWTF is the Java compiler insisting that any method throwing a Throwable must use "throws Throwable" even through Throwable shouldn’t be caught by the caller.

    "throws Exception" should suffice in that case. After all, a Throwable which isn’t an Exception is an Error, and any method call can throw an Error.

     

    There could be more subclasses to Throwable. Still, a better way to handle this would be throw new RuntimeException(e)

     



  • @VinDuv said:

    After all, a Throwable which isn’t an Exception is an Error, and any method call can throw an Error.

     

     Well, no!

     

    public class MikeThrow extends Throwable {

    //.....

    }

     



  • @derari said:

    @Faxmachinen said:

    While I was at it, I also removed some empty catch blocks. Here's one of those:

    String[ ] PREFIX = {"get", "is"}
    for (String s : PREFIX) {
        try {
            return type.getMethod(s + methodName);
        } catch (NoSuchMethodException e) {
            // ignore
        }
    }
    return null;

     

    This code looks right the way it is. "Iterate over possible method names, return if the method exists; if none was found return null". If you remove the catch, the loop can never continue.

     

    Seconded. You're changing the semantics here. The original code returns a method, or null; the new code throws an exception if "get" is not found. Tell me that there are unit tests for this method??? If not, you wrote unit tests before making your changes??? And that the unit tests are now failing??? That's what I would expect.


  • Winner of the 2016 Presidential Election

    @DrPepper said:

    Seconded. You're changing the semantics here. The original code returns a method, or null; the new code throws an exception if "get" is not found. Tell me that there are unit tests for this method??? If not, you wrote unit tests before making your changes??? And that the unit tests are now failing??? That's what I would expect.

    It's changing the contract of the method, so Java should force him to add another exception to the list of possible thrown exceptions, which should force the calling code to deal with it. Of course if the catch { // do nothing } pattern is prevalent, or the method just throws Exception, that safety net is gone.

    Either way, not something you can just rip out without thoroughly retesting everything.



  • @joe.edwards said:

    so Java should force him to add another exception to the list of possible thrown exceptions

    What I love about Java. Suppose someone deep down in the code adds another exception that could be thrown by a method; then each caller of that method must either handle the exception or declare it as possibly being thrown by the caller. So the change has to ripple up through the code until you reach the appropriate place to catch it.



  • @DrPepper said:

    @derari said:

    @Faxmachinen said:

    While I was at it, I also removed some empty catch blocks. Here's one of those:

    String[ ] PREFIX = {"get", "is"}
    for (String s : PREFIX) {
        try {
            return type.getMethod(s + methodName);
        } catch (NoSuchMethodException e) {
            // ignore
        }
    }
    return null;

     

    This code looks right the way it is. "Iterate over possible method names, return if the method exists; if none was found return null". If you remove the catch, the loop can never continue.

     

    Seconded. You're changing the semantics here. The original code returns a method, or null; the new code throws an exception if "get" is not found. Tell me that there are unit tests for this method??? If not, you wrote unit tests before making your changes??? And that the unit tests are now failing??? That's what I would expect.

    Agreed... he cannot change this. 

    I deal with java exceptions in old code all the time, and I love adding logging or simply removing empty catch blocks.  However in this case, the purpose of the catch is to allow you to proceed to the next iteration of the loop.  The common scenario is tried first ("get*"), and if that fails you'll swallow the exception and move to "is*".  I would still like to see a "log.warn" statement inside the catch block, on the principle that no catch block should be completely silent, but don't put a stack trace in the log over this.  

     

    However, I don't like returning null.  I think you should actually do this:  

    String[ ] PREFIX = {"get", "is"}
    for (String s : PREFIX) {
    try {
    return type.getMethod(s + methodName);
    } catch (NoSuchMethodException e) {
    log.warn("didn't find " + s + methodName);
    }
    }
    throw new NoSuchMethodException("No get* or is* for " + methodName);

    this prevents Null Pointer Exceptions from cropping up in unrelated parts of the code. 

    In general, never return null, always return a good value or throw an exception.  



  • I am not a Java expert, but this means it is then easier to deal with in .Net as all exceptions derrive from System.Exception and there isn't the concept of "errors" like this, or am I missing something.



  • @The Bytemaster said:

    I am not a Java expert, but this means it is then easier to deal with in .Net as all exceptions derrive from System.Exception and there isn't the concept of "errors" like this, or am I missing something.

     

    Personally, I like the distinction between Exceptions and Errors. An exception tells you that some code, for some specified reason, failed and sometimes you anticpated this case and are now able to do some workaround. An error, on the other hand, tells you that the VM failed. It is equivalent to a blue screen: the entire program may be corrupted and the only thing left to do is to carefully shut down. That's why you are not supposed to catch Errors (or Throwables), because there is usually nothing reasonable that you can do anyway.

    The problem with Java are the checked exceptions: every Throwable that is not a subclass of Error or RuntimeException has to be declared as part of the method signature when it might be thrown. While there are good reasons and well intentions behind this feature, it is annoying as fuck.

     



  • @DrPepper said:

    What I love about Java. Suppose someone deep down in the code adds another exception that could be thrown by a method; then each caller of that method must either handle the exception or declare it as possibly being thrown by the caller. So the change has to ripple up through the code until you reach the appropriate place to catch it.

    You love checked exceptions? You do realize that it adds a ton of boilerplate with arguably no benefit, right? Your whole "ripple up" is a bunch of changes to intermediate classes that have nothing to do with the exception and don't care, but still have to be declared to throw the new exception. Especially considering a lot of times exceptions are only going to be handled in a few places, it's really quite an awful trade-off. That's why no other languages use checked exceptions.



  • @derari said:

    I like the distinction between Exceptions and Errors.

    @derari said:

    An error, on the other hand, tells you that the VM failed. It is equivalent to a blue screen: the entire program may be corrupted and the only thing left to do is to carefully shut down. That's why you are not supposed to catch Errors (or Throwables), because there is usually nothing reasonable that you can do anyway.

    So basically Errors are completely useless. If you can't do anything reasonable with them, then why even expose them in the language?



  • @DrPepper said:

    What I love about Java. Suppose someone deep
    down in the code adds another exception that could be thrown by a
    method; then each caller of that method must either handle the exception
    or declare it as possibly being thrown by the caller. So the change
    has to ripple up through the code until you reach the appropriate place
    to catch it.

    I like the idea behind them. It forces you to clean up your shit. But it turns out, cleaning up the shit requires work and is most often not worth it, so I guess I don't like them in practice.



  • @morbiuswilters said:

    @DrPepper said:
    What I love about Java. Suppose someone deep down in the code adds another exception that could be thrown by a method; then each caller of that method must either handle the exception or declare it as possibly being thrown by the caller. So the change has to ripple up through the code until you reach the appropriate place to catch it.

    You love checked exceptions? You do realize that it adds a ton of boilerplate with arguably no benefit, right? Your whole "ripple up" is a bunch of changes to intermediate classes that have nothing to do with the exception and don't care, but still have to be declared to throw the new exception. Especially considering a lot of times exceptions are only going to be handled in a few places, it's really quite an awful trade-off. That's why no other languages use checked exceptions.

     

    the correct way to handle this is with encapsulation, it's number two on this list:  http://www.wikijava.org/wiki/10_best_practices_with_Exceptions

    I've been dealing with checked exceptions for so long that it's just part of the language to me.  When used correctly, I think they increase readability.  When used incorrectly, they cause bloat.



  •  @morbiuswilters said:

    @derari said:
    I like the distinction between Exceptions and Errors.

    @derari said:

    An error, on the other hand, tells you that the VM failed. It is equivalent to a blue screen: the entire program may be corrupted and the only thing left to do is to carefully shut down. That's why you are not supposed to catch Errors (or Throwables), because there is usually nothing reasonable that you can do anyway.

    So basically Errors are completely useless. If you can't do anything reasonable with them, then why even expose them in the language?

    Why create the ability to catch "OutOfMemoryError"?  So bad programmers will use that ability, and thereby separate themselves.  But seriously, I've had to catch StackOverflowError before in runaway regexes.  It's not pretty, but that's an example of an error which is recoverable (when the exception is caught, the stack space has already been freed).  



  • @morbiuswilters said:

    What I love about Java.
    You missed the "sarcasm" markup. I guess it didn't show up clearly on community server; I'll try to make it "bold" next time so it's more obvious.



  • @leonardo said:

    the correct way to handle this is with encapsulation, it's number two on this list:

    So your solution to boilerplate code that does nothing of value is more boilerplate code that does nothing of value?

    @leonardo said:

    When used correctly, I think they increase readability.

    Please explain how having 10 levels of methods which list four exceptions they throw improves readability. I mean, the methods aren't handling the exception themselves, so why do we need to state that fact? It's literally just pointless busywork.



  • @leonardo said:

    But seriously, I've had to catch StackOverflowError before in runaway regexes.

    Fix the regex?

    @leonardo said:

    It's not pretty, but that's an example of an error which is recoverable (when the exception is caught, the stack space has already been freed).

    This is actually bad encapsulation on the part of the Regex class. It shouldn't be propagating stack errors up to me. It should throw something like an PatternException which tells me that the regex used too much stack space. Yes, a regex error is recoverable, but it should be an exception, not a JVM error.



  • @DrPepper said:

    @morbiuswilters said:
    What I love about Java.
    You missed the "sarcasm" markup. I guess it didn't show up clearly on community server; I'll try to make it "bold" next time so it's more obvious.

    Apologies. Sadly, what you wrote actually sounded like the defenses of checked exceptions I've read. "But it makes you explicitly handle each exception!"



  • @morbiuswilters said:

    @leonardo said:
    the correct way to handle this is with encapsulation, it's number two on this list:

    So your solution to boilerplate code that does nothing of value is more boilerplate code that does nothing of value?

    @leonardo said:

    When used correctly, I think they increase readability.

    Please explain how having 10 levels of methods which list four exceptions they throw improves readability. I mean, the methods aren't handling the exception themselves, so why do we need to state that fact? It's literally just pointless busywork.

     

    You don't have multiple types of exceptions being thrown (any method that throws 4 different exceptions is horribly bloated).  It works like this, say at the top level you have a catch block that handles "UserException" (custom defined stuff), and this is the only type of exception that you need to explicitly throw in most cases.  So, sure, your middle-layer code all throws this exception.  If you add a new low-level method that parses a String into an Integer, you wrap the possible NumberFormatException and throw it as a UserException.  Bingo, none of the middle layers have to change!

     

    @morbiuswilters said:

    [quote
    user="leonardo"]But seriously, I've had to catch StackOverflowError
    before in runaway regexes.

    Fix the regex?

    @leonardo said:

    It's not pretty, but that's an example of an
    error which is recoverable (when the exception is caught, the stack
    space has already been freed).

    This is actually bad encapsulation on the part of the Regex class. It
    shouldn't be propagating stack errors up to me. It should throw
    something like an PatternException which tells me that the regex used
    too much stack space. Yes, a regex error is recoverable, but it should
    be an exception, not a JVM error.[/quote] 

    So, you're saying that
    the regex class should catch the StackOverflowException, and encapsulate
    it as a Pattern Exception.  You've made both of my cases for me!  The distinction between a 'catchable' error/exception and one that you 'should not catch' is the difference between "recoverable" and not.  (By the way, fixing the regex was the first step, it was catastrophic backtracking and solved by a possessive operator.) 



  • @leonardo said:

    You don't have multiple types of exceptions being thrown (any method that throws 4 different exceptions is horribly bloated).  It works like this, say at the top level you have a catch block that handles "UserException" (custom defined stuff), and this is the only type of exception that you need to explicitly throw in most cases.  So, sure, your middle-layer code all throws this exception.  If you add a new low-level method that parses a String into an Integer, you wrap the possible NumberFormatException and throw it as a UserException.  Bingo, none of the middle layers have to change!

    But.. that completely undermines checked exceptions. And it defeats the whole purpose of having multiple types of exceptions, which is allowing handling of different types. And I've seen plenty of methods that throw 4 types of exception. The higher-level the code, the more likely lower levels might bubble up an IOException, SQLException, etc.. Of course, that code is using checked exceptions the way they are meant to be used, for better or worse.

    @leonardo said:

    So, you're saying that
    the regex class should catch the StackOverflowException, and encapsulate
    it as a Pattern Exception.  You've made both of my cases for me!  The distinction between a 'catchable' error/exception and one that you 'should not catch' is the difference between "recoverable" and not.

    No, I'm not making your case. I'm saying that non-recoverable errors shouldn't even be surfaced. The SOE was recoverable, but it's also revealing too much detail about the inner-workings of the regex classes.



  • @morbiuswilters said:

    @derari said:
    I like the distinction between Exceptions and Errors.

    @derari said:

    An error, on the other hand, tells you that the VM failed. It is equivalent to a blue screen: the entire program may be corrupted and the only thing left to do is to carefully shut down. That's why you are not supposed to catch Errors (or Throwables), because there is usually nothing reasonable that you can do anyway.

    So basically Errors are completely useless. If you can't do anything reasonable with them, then why even expose them in the language?

     

    The purpose of Errors is that you get to run your finalizers (eg, to release external resources). They have to be part of the language so that the program can continue and shut down gracefully. By giving them an extra branch in the hierarchy, you avoid that a lazy programmer catches them. And sometimes, you may even know that your program is not currupted (eg, a SO in a Regex or an OutOfMemory when you allocate large arrays) and you can catch them reasonably.

    What alternatives would you prefer? exit(0)?



  •  @derari said:

    And sometimes, you may even know that your program is not currupted (eg, a SO in a Regex or an OutOfMemory when you allocate large arrays) and you can catch them reasonably.

    What alternatives would you prefer? exit(0)?

    You cannot catch OOM errors in a multi-threaded application.  I read a good description a long time ago and I've seen bad things happen in our application before.  The underlying problem is that you don't know what other threads have seen OOM before your thread threw the error and what other effects that has had to the application.  This is what I've seen happen... Let's say you write some code to run in Tomcat, re-allocating a large array to add a few K at a time and you get close to OOM.  Your thread pauses, and now Tomcat's threads run and they get an OOM, like the thread that listens for TCP connections.  Your thread resumes, dies with OOM, and you think everything is okay because your code stopped eventually right?  Nope, now none of your users can access your site because it won't respond to TCP connections any more.  The only thin you can do in these cases is to print some debugging info for your support team and trigger something that will restart your application. Other errors like SO are not so bad though.

     

     



  • @derari said:

    The purpose of Errors is that you get to run your finalizers (eg, to release external resources).

    Unless, of course, the JVM is actually OOM or some other condition you can't reasonably run finalizers from. So you still have to design your application to be able to deal with unreleased external resources.

    @derari said:

    SO in a Regex

    As I already said, that's bad encapsulation.

    @derari said:

    OutOfMemory when you allocate large arrays

    Yeah, you can catch that, but then what are you going to do? "Oh, I guess I didn't really need that array after all.."?



  • @NatmanZ8 said:

    Other errors like SO are not so bad though.

    I don't see why it exists. Either you have too much recursion and you need to increase the stack size* or it's something like a regex that causes too much recursion, in which case it should be a better-encapsulated exception that allows you to recover and keep going.

    (*And let me say: WTF Java? Why do I even have to have a static stack size on a 64-bit arch?)



  • @morbiuswilters said:

    @NatmanZ8 said:
    Other errors like SO are not so bad though.

    I don't see why it exists. Either you have too much recursion and you need to increase the stack size* or it's something like a regex that causes too much recursion, in which case it should be a better-encapsulated exception that allows you to recover and keep going.

    (*And let me say: WTF Java? Why do I even have to have a static stack size on a 64-bit arch?)

    Go has no limit on stack size other than available memory.


  • Winner of the 2016 Presidential Election

    @Ben L. said:

    Go has no limit on stack size other than available memory.

    This seems like another terrible idea. Most stack overflows aren't caused by a stack that's too small, but by some form of infinite recursion. In that case, more stack space isn't going to help. It just means that the more likely case is now going to be more destructive (chew through all your available memory) rather than simply halt. I've seen something similar happen in Javascript, where, rather than simply give up, it proceeded to lag out the entire browser.



  • @morbiuswilters said:


    @derari said:

    SO in a Regex

    As I already said, that's bad encapsulation.

    True, Regex did it wrong. But other developers who code stack-heavy algorithms have a chance to do it better.

     

    Yeah, you can catch that, but then what are you going to do? "Oh, I guess I didn't really need that array after all.."?
    What about showing a useful error message, like "The data is to fucking big, but don't worry, your other stuff isn't lost. Next time try '-Xmx1000G'."

    The difference is that you converted a Holy-Shit-The-VM-Is-On-Fire into a Sorry-This-Algorithm-Failed.



  • @leonardo said:

    @DrPepper said:
    @derari said:

    @Faxmachinen said:

    ...

    This code looks right the way it is.

    Seconded. You're changing the semantics here.

    Agreed... he cannot change this.

    You guys are morons. For the record, here is the replacement:

    String[‍] PREFIX = {"get", "is"}
    Method[‍] methods = type.getMethods();
    for (Method method : methods)
    {
    	for (String s : PREFIX) {
    		String name = s + methodName;
    		if (name.equals(method.getName()))
    			return method;
    	}
    }
    return null;


  • @NatmanZ8 said:

    You cannot catch OOM errors in a multi-threaded application.  I read a good description a long time ago and I've seen bad things happen in our application before.  The underlying problem is that you don't know what other threads have seen OOM before your thread threw the error and what other effects that has had to the application.  This is what I've seen happen... Let's say you write some code to run in Tomcat, re-allocating a large array to add a few K at a time and you get close to OOM.  Your thread pauses, and now Tomcat's threads run and they get an OOM, like the thread that listens for TCP connections.  Your thread resumes, dies with OOM, and you think everything is okay because your code stopped eventually right?  Nope, now none of your users can access your site because it won't respond to TCP connections any more.  The only thin you can do in these cases is to print some debugging info for your support team and trigger something that will restart your application. Other errors like SO are not so bad though.

     

    If the TCP-listener thread dies from an OOM, why is it my responsibilityto restart it? It's clearly Tomcat that is broken. In your example, the major difference between Exceptions and Errors is ignored: If a thread dies from Exeption, sorry for that thread; if a thread dies from Error, you have shut down the VM.

     



  • @Faxmachinen said:

    String[‍] PREFIX = {"get", "is"}
    Method[‍] methods = type.getMethods();
    for (Method method : methods)
    {
    for (String s : PREFIX) {
    String name = s + methodName;
    if (name.equals(method.getName()))
    return method;
    }
    }
    return null;

     

    Nice job! If the method is overloaded to accept paramters, you risk returning the wrong one.



  • @derari said:

    Nice job! If the method is overloaded to accept paramters, you risk returning the wrong one.

    You are correct. TRWTF etc.



  • @derari said:

    @Faxmachinen said:

    String[‍] PREFIX = {"get", "is"}
    Method[‍] methods = type.getMethods();
    for (Method method : methods)
    {
    for (String s : PREFIX) {
    String name = s + methodName;
    if (name.equals(method.getName()))
    return method;
    }
    }
    return null;

     

    Nice job! If the method is overloaded to accept paramters, you risk returning the wrong one.

    I don't know Java introspection well enough to understand why you say it depends on whether the method is overloaded.  The original version always returned get* if both get* and is* existed.  This version returns whichever is first in the array returned by getMethods, ...

    [quote user="http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getMethods()"]The elements in the array
    returned are not sorted and are not in any particular order.[/quote] ... so oops, regardless of overloads, surely?

     



  • @DaveK said:

    I don't know Java introspection well enough to understand why you say it depends on whether the method is overloaded.

    If a class defines getFoo() and getFoo(int i), the original code would only return the former, while the "fixed" code might return either of them.

    The original version always returned get* if both get* and is* existed. This version returns whichever is first in the array

    True, this is also a change to the semantics, but the program will still run. With the unexpected parameter, calling the method will fail.

     



  • I much prefer the pattern of throwing an exception, rather than returning null:

    String[‍] PREFIX = {"get", "is"}
    Method[‍] methods = type.getMethods();
    for (Method method : methods)
    {
    for (String s : PREFIX) {
    String name = s + methodName;
    if (name.equals(method.getName()))
    return method;
    }
    }
    throw new NoSuchMethodException();//////don't return null, that would expose null-pointer-exceptions to all calling methods, or create MORE boilerplate code as they all check for non-null
     

    As to whether or not introspection is going to give better results than the other way, I'd have to test.  



  •  @derari said:

    if a thread dies from Error, you have shut down the VM.

     

     That's exactly what I said.  When you get an OOM error, you must stop the whole JVM and restart it.  There is no way to recover from OOM.  In my example, even if the TCP listener thread caught the OOM, it could not do anything reasonable until the bad thread gives up some memory.  If there is enough pressure on memory, even if the listener thread tried to just stop itself and wait until some memory was free, that error handling may not have enough free memory to run at all.

    In the post that I quoted earlier, you said that you may know that the program is not corrupted and used OOM on an array allocation as an example, and I added that you cannot recover from OOM when multiple threads are involved. SO is different because when the error is thrown the stack is unwound and thus freeing up the resource, but OOM does not necessarily free up the cause of the problem and so a VM restart is the only safe solution.

     

     


  • Discourse touched me in a no-no place

    @NatmanZ8 said:

    When you get an OOM error, you must stop the whole JVM and restart it.
    Usually. Not always. Depends on the details, but recovery is always a whole-application issue, and can't be done on any smaller scale. (Most OOMs are either due to problems unloading classes or a thread that's holding far more memory than anyone expected; these are both unpleasant to hunt down, even with good tools.)

    Other errors are more recoverable.



  • @dkf said:

    @NatmanZ8 said:
    When you get an OOM error, you must stop the whole JVM and restart it.
    Usually. Not always. Depends on the details, but recovery is always a whole-application issue, and can't be done on any smaller scale. (Most OOMs are either due to problems unloading classes or a thread that's holding far more memory than anyone expected; these are both unpleasant to hunt down, even with good tools.)

    Other errors are more recoverable.

    try { /* allocate 100MB byte array */ }
    catch (OutOfMemoryError ex) { /* whoops */ }


  • @Ben L. said:

    @dkf said:
    @NatmanZ8 said:
    When you get an OOM error, you must stop the whole JVM and restart it.
    Usually. Not always. Depends on the details, but recovery is always a whole-application issue, and can't be done on any smaller scale. (Most OOMs are either due to problems unloading classes or a thread that's holding far more memory than anyone expected; these are both unpleasant to hunt down, even with good tools.)

    Other errors are more recoverable.

    try { /* allocate 100MB byte array */ }
    catch (OutOfMemoryError ex) { /* whoops */ }

    And how exactly is that incompatible with not always or most? I don't know where this comes from, but I've noticed that with many programmers unless they frequently get involved in meetings with business people there is a simplistic mindset that sets in and makes all discussions very tedious.


    Some guy: The sun does not always shine.
    Programmer: The sun is shining right now so you are wrong, moron

  • Discourse touched me in a no-no place

    @Ben L. said:

    try { /* allocate 100MB byte array / }
    catch (OutOfMemoryError ex) { /
    whoops */ }
    The problem with that is that you can't be sure that the OOM will just hit the thread trying to do that alloc; it might smash flat a few others at the same time.

    Damn you, global resource pool! How dare you have non-local effects!?



  • @NatmanZ8 said:

    That's exactly what I said.

    No, it's not. There are two OOMs in your example. The one that happens in my thread is safe to catch, the one that happens in the TCP thread is not.

    I did not say every OOM can be caught, I said as long as the error only occurs where you expect it, you're safe. If other threads shut down because of errors (or any other reason), the application has to decide whether to shut down or restart the threads. If the webserver instead decides to continue without listening for TCP requests, it's a bug in the webserver.




  • @dkf said:

    @Ben L. said:
    try { /* allocate 100MB byte array / }
    catch (OutOfMemoryError ex) { /
    whoops */ }
    The problem with that is that you can't be sure that the OOM will just hit the thread trying to do that alloc; it might smash flat a few others at the same time.

    Damn you, global resource pool! How dare you have non-local effects!?

    OOM only happens when you try to allocate memory. So as long as you don't make any new objects or call any functions, you should be fine.



  • @derari said:

    What about showing a useful error message, like "The data is to fucking big, but don't worry, your other stuff isn't lost. Next time try '-Xmx1000G'."

    The difference is that you converted a Holy-Shit-The-VM-Is-On-Fire into a Sorry-This-Algorithm-Failed.

    An out of memory error will tell you that in the first place. I just don't know it needs to be exposed to the programmer.



  • @derari said:

    If the webserver instead decides to continue without listening for TCP requests, it's a bug in the webserver.

    But that just brings us back to the original point: the web server shuts terminates the process and now the OOM isn't recoverable.


  • Discourse touched me in a no-no place

    @Ben L. said:

    OOM only happens when you try to allocate memory. So as long as you don't make any new objects or call any functions, you should be fine.
    I'm not saying you won't get away with it many times, and a failing single large allocation is more likely to be recoverable from than a small failing allocation (since when that happens, you're out of memory and out of luck), but it simply isn't safe. Plus, if you're using Java then you can't be sure that you're not allocating objects, or that a library you're using (including the built-in system library) isn't allocating objects. You can hardly do anything much in Java without allocating objects. That's how the language is designed (and the implementors have made allocation really cheap, which sounds easy and isn't).

    That said, my OOMs seem to always be related to dratted PermGen space, and IME those just kill your code completely. Except they leave the process sitting there doing zip and not responding to (normal) signals. Ugh.



  • @morbiuswilters said:

    @derari said:

    What about showing a useful error message, like "The data is to fucking big, but don't worry, your other stuff isn't lost. Next time try '-Xmx1000G'."

    The difference is that you converted a Holy-Shit-The-VM-Is-On-Fire into a Sorry-This-Algorithm-Failed.

    An out of memory error will tell you that in the first place. I just don't know it needs to be exposed to the programmer.

     

    Again, I'm asking you: What do you think should happen when the VM fails to allocate an object?

     



  • @morbiuswilters said:

    @derari said:
    If the webserver instead decides to continue without listening for TCP requests, it's a bug in the webserver.

    But that just brings us back to the original point: the web server shuts terminates the process and now the OOM isn't recoverable.

     

    Yes, that OOM is not recoverable. The webserver should try to cleanly close all open connections and shut down. Which it didn't (in Natman's example), because it was buggy. Which isn't my fault.

    Turns out 95% of all errors are not recoverable, even if they are only exceptions. That's why Java's checked excpetions are so pointless.


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.