String s1 = "Stack";
"Stack" will be in the String Constant pool.
String s2 = s1 + " Overflow";
Internally + operator uses StringBuilder for concatenating strings.
So internal implementation  String s2 = s1 + " Overflow"; will be
String str = new StringBuilder(s1).append("Overflow").toString(); 
Here since it is new StringBuilder(str) so the StringBuilder object will be created in the Heap. Lets look into StringBuilder(String str) constructor
public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
} 
and super(int capacity) constructor
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}
Here we can see that the StringBuilder(String str) constructor just creating a char[] array and calling append(String str) method.
If we look at the implementation of append(String str) method of StringBuilder we can see that the append(String str) method is just playing with a char[] array and it's not creating any new object or array.
public StringBuilder append(String str) {
    super.append(str);
    return this;
} 
and the implementation of super.append(String str) is
public AbstractStringBuilder append(String str) {
    if (str == null) str = "null";
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}
At the end lets look at the toString() method StringBuilder class.
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}
The toString() method of StringBuilder is returning a new String which will be definitely in the Heap since it is created with new String(...);
The above explanation says that The StringBuilder will only create a new string when toString() is called on it. Until then, it maintains an char[] array of all the elements added to it.
So, the conclusion is "Stack" will be in the String Constant pool and s1 + " Overflow" i.e Stack overflow will be in the Heap.