I don’t see a difference between pass by copy and pass by value.
The mutability difference is that part of a struct can be modified in place, which value classes can’t: the value of a complete value-class variable (or array slot) can only be modified (reassigned) as a whole. This is presumably because object references to value-class objects can be created, and those objects should be immutable so their identity doesn’t matter.
I think that's mostly a semantic difference - Java avoided the problem of strange lifetimes, captures, tearing by fixing the semantics as immutable value objects, while C# has to deal with these issues.
But under the hood it can (and will) do a modification in place.
I think pass by copy is a consequence of being modifiable.
The other solution is to stack allocate and pass a pointer but as i said, unlike in C#, i do not think it's possible to do that in Java.
In Go, you can stack allocate but when you send a pointer (that escapes), the compiler will heap allocate the object.