Aren't there other ways for a stack overflow to occur, not only
  through recursion?
Challenge accepted :) StackOverflowError without recursion (challenge failed, see comments):
public class Test
{
    final static int CALLS = 710;
    public static void main(String[] args)
    {
        final Functor[] functors = new Functor[CALLS];
        for (int i = 0; i < CALLS; i++)
        {
            final int finalInt = i;
            functors[i] = new Functor()
            {
                @Override
                public void fun()
                {
                    System.out.print(finalInt + " ");
                    if (finalInt != CALLS - 1)
                    {
                        functors[finalInt + 1].fun();
                    }
                }
            };
        }
        // Let's get ready to ruuuuuuumble!
        functors[0].fun(); // Sorry, couldn't resist to not comment in such moment. 
    }
    interface Functor
    {
        void fun();
    }
}
Compile with standard javac Test.java and run with java -Xss104k Test 2> out. After that, more out will tell you:
Exception in thread "main" java.lang.StackOverflowError
Second try.
Now the idea is even simpler. Primitives in Java can be stored on the stack. So, let's declare a lot of doubles, like double a1,a2,a3.... This script can write, compile and run the code for us:
#!/bin/sh
VARIABLES=4000
NAME=Test
FILE=$NAME.java
SOURCE="public class $NAME{public static void main(String[] args){double "
for i in $(seq 1 $VARIABLES);
do
    SOURCE=$SOURCE"a$i,"
done
SOURCE=$SOURCE"b=0;System.out.println(b);}}"
echo $SOURCE > $FILE
javac $FILE
java -Xss104k $NAME
And... I got something unexpected:
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f4822f9d501, pid=4988, tid=139947823249152
#
# JRE version: 6.0_27-b27
# Java VM: OpenJDK 64-Bit Server VM (20.0-b12 mixed mode linux-amd64 compressed oops)
# Derivative: IcedTea6 1.12.6
# Distribution: Ubuntu 10.04.1 LTS, package 6b27-1.12.6-1ubuntu0.10.04.2
# Problematic frame:
# V  [libjvm.so+0x4ce501]  JavaThread::last_frame()+0xa1
#
# An error report file with more information is saved as:
# /home/adam/Desktop/test/hs_err_pid4988.log
#
# If you would like to submit a bug report, please include
# instructions how to reproduce the bug and visit:
#   https://bugs.launchpad.net/ubuntu/+source/openjdk-6/
#
Aborted
It's 100% repetitive. This is related to your second question:
Does the StackOverflowError happen before the JVM actually overflows
  the stack or after?
So, in case of OpenJDK 20.0-b12 we can see that JVM firstly exploded. But it seems like a bug, maybe someone can confirm that in comments please, because I'm not sure. Should I report this? Maybe it's already fixed in some newer version... According to JVM specification link (given by JB Nizet in a comment) JVM should throw a StackOverflowError, not die:
If the computation in a thread requires a larger Java Virtual Machine
  stack than is permitted, the Java Virtual Machine throws a
  StackOverflowError.
Third try.
public class Test {
    Test test = new Test();
    public static void main(String[] args) {
        new Test();
    }
}
We want to create new Test object. So, its (implicit) constructor will be called. But, just before that, all the members of Test are initialized. So, Test test = new Test() is executed first...
We want to create new Test object...
Update: Bad luck, this is recursion, I asked question about that here.